Skip to content

Commit f344221

Browse files
committedJan 10, 2022
Sync
1 parent 5a8adc4 commit f344221

20 files changed

+1246
-772
lines changed
 

‎backend/core/config.ts

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
type Keys = "RDS_SECRET" | "RDS_ARN" | "RDS_DATABASE";
2+
3+
export function config(key: Keys) {
4+
const value = process.env[key];
5+
if (!value) throw new Error(`Missing environment variable "${key}"`);
6+
return value;
7+
}

‎backend/core/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export * from "./context";
22
export * from "./todo";
3+
export * from "./sql";

‎backend/core/sql/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * as SQL from "./sql";

‎backend/core/sql/sql.ts

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { Kysely } from "kysely";
2+
import { DataApiDialect } from "kysely-data-api";
3+
import { config } from "../config";
4+
import RDSDataService from "aws-sdk/clients/rdsdataservice";
5+
6+
export type Database = {};
7+
8+
export const DB = new Kysely<Database>({
9+
dialect: new DataApiDialect({
10+
mode: "postgres",
11+
driver: {
12+
client: new RDSDataService(),
13+
database: config("RDS_DATABASE"),
14+
secretArn: config("RDS_SECRET"),
15+
resourceArn: config("RDS_ARN"),
16+
},
17+
}),
18+
});

‎backend/package.json

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
"dependencies": {
99
"@types/aws-lambda": "^8.10.83",
1010
"apollo-server-lambda": "^3.6.1",
11+
"kysely": "^0.15.3",
12+
"kysely-data-api": "^0.0.7",
1113
"lodash-es": "^4.17.21"
1214
},
1315
"devDependencies": {

‎backend/services/apollo/apollo.ts

+11-4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
import { ApolloServer } from "apollo-server-lambda";
2-
import { TodoResolver } from "./todo";
3-
import { UserResolver } from "./user";
4-
import { SessionResolver } from "./session";
52
import { typeDefs } from "./schema";
63
import { useContext } from "@acme/core";
74
import { merge } from "lodash-es";
85

9-
const resolvers = merge([TodoResolver, UserResolver, SessionResolver]);
6+
import { TodoResolver } from "./todo";
7+
import { UserResolver } from "./user";
8+
import { SessionResolver } from "./session";
9+
import { DebugResolver } from "./debug";
10+
11+
const resolvers = merge([
12+
TodoResolver,
13+
UserResolver,
14+
SessionResolver,
15+
DebugResolver,
16+
]);
1017

1118
const server = new ApolloServer({
1219
typeDefs,

‎backend/services/apollo/debug.ts

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { Context, SQL } from "@acme/core";
2+
import { Resolvers } from "./types";
3+
4+
export const DebugResolver: Resolvers<Context> = {
5+
Query: {
6+
debug: async () => {
7+
return {};
8+
},
9+
},
10+
Debug: {
11+
scrap: async () => {
12+
const result = await SQL.DB.raw("SELECT 1").execute();
13+
return JSON.stringify(result);
14+
},
15+
},
16+
};

‎backend/services/apollo/schema.ts

+4
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,14 @@ export const typeDefs = gql`
99
title: String!
1010
user: String!
1111
}
12+
type Debug {
13+
scrap: String!
14+
}
1215
type Mutation {
1316
createTodo(input: CreateTodoInput!): Todo!
1417
}
1518
type Query {
19+
debug: Debug!
1620
session: Session!
1721
user(id: ID!): User!
1822
}

‎backend/services/apollo/types.ts

+32-14
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ export type CreateTodoInput = {
2929
user: Scalars["String"];
3030
};
3131

32+
export type Debug = {
33+
__typename?: "Debug";
34+
scrap: Scalars["String"];
35+
};
36+
3237
export type Mutation = {
3338
__typename?: "Mutation";
3439
createTodo: Todo;
@@ -40,6 +45,7 @@ export type MutationCreateTodoArgs = {
4045

4146
export type Query = {
4247
__typename?: "Query";
48+
debug: Debug;
4349
session: Session;
4450
user: User;
4551
};
@@ -175,28 +181,38 @@ export type DirectiveResolverFn<
175181

176182
/** Mapping between all available schema types and the resolvers types */
177183
export type ResolversTypes = ResolversObject<{
178-
Boolean: ResolverTypeWrapper<Scalars["Boolean"]>;
179-
CreateTodoInput: CreateTodoInput;
180-
ID: ResolverTypeWrapper<Scalars["ID"]>;
184+
Boolean: ResolverTypeWrapper<Partial<Scalars["Boolean"]>>;
185+
CreateTodoInput: ResolverTypeWrapper<Partial<CreateTodoInput>>;
186+
Debug: ResolverTypeWrapper<Partial<Debug>>;
187+
ID: ResolverTypeWrapper<Partial<Scalars["ID"]>>;
181188
Mutation: ResolverTypeWrapper<{}>;
182189
Query: ResolverTypeWrapper<{}>;
183-
Session: ResolverTypeWrapper<Session>;
184-
String: ResolverTypeWrapper<Scalars["String"]>;
185-
Todo: ResolverTypeWrapper<Todo>;
186-
User: ResolverTypeWrapper<User>;
190+
Session: ResolverTypeWrapper<Partial<Session>>;
191+
String: ResolverTypeWrapper<Partial<Scalars["String"]>>;
192+
Todo: ResolverTypeWrapper<Partial<Todo>>;
193+
User: ResolverTypeWrapper<Partial<User>>;
187194
}>;
188195

189196
/** Mapping between all available schema types and the resolvers parents */
190197
export type ResolversParentTypes = ResolversObject<{
191-
Boolean: Scalars["Boolean"];
192-
CreateTodoInput: CreateTodoInput;
193-
ID: Scalars["ID"];
198+
Boolean: Partial<Scalars["Boolean"]>;
199+
CreateTodoInput: Partial<CreateTodoInput>;
200+
Debug: Partial<Debug>;
201+
ID: Partial<Scalars["ID"]>;
194202
Mutation: {};
195203
Query: {};
196-
Session: Session;
197-
String: Scalars["String"];
198-
Todo: Todo;
199-
User: User;
204+
Session: Partial<Session>;
205+
String: Partial<Scalars["String"]>;
206+
Todo: Partial<Todo>;
207+
User: Partial<User>;
208+
}>;
209+
210+
export type DebugResolvers<
211+
ContextType = Context,
212+
ParentType extends ResolversParentTypes["Debug"] = ResolversParentTypes["Debug"]
213+
> = ResolversObject<{
214+
scrap?: Resolver<ResolversTypes["String"], ParentType, ContextType>;
215+
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
200216
}>;
201217

202218
export type MutationResolvers<
@@ -215,6 +231,7 @@ export type QueryResolvers<
215231
ContextType = Context,
216232
ParentType extends ResolversParentTypes["Query"] = ResolversParentTypes["Query"]
217233
> = ResolversObject<{
234+
debug?: Resolver<ResolversTypes["Debug"], ParentType, ContextType>;
218235
session?: Resolver<ResolversTypes["Session"], ParentType, ContextType>;
219236
user?: Resolver<
220237
ResolversTypes["User"],
@@ -251,6 +268,7 @@ export type UserResolvers<
251268
}>;
252269

253270
export type Resolvers<ContextType = Context> = ResolversObject<{
271+
Debug?: DebugResolvers<ContextType>;
254272
Mutation?: MutationResolvers<ContextType>;
255273
Query?: QueryResolvers<ContextType>;
256274
Session?: SessionResolvers<ContextType>;

‎backend/services/rest/debug.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export function scrap() {}

‎cdk.context.json

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"availability-zones:account=280826753141:region=us-east-2": [
3+
"us-east-2a",
4+
"us-east-2b",
5+
"us-east-2c"
6+
]
7+
}

‎frontend/src/data/urql.ts

+6
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ export type CreateTodoInput = {
2727
user: Scalars["String"];
2828
};
2929

30+
export type Debug = {
31+
__typename?: "Debug";
32+
scrap: Scalars["String"];
33+
};
34+
3035
export type Mutation = {
3136
__typename?: "Mutation";
3237
createTodo: Todo;
@@ -38,6 +43,7 @@ export type MutationCreateTodoArgs = {
3843

3944
export type Query = {
4045
__typename?: "Query";
46+
debug: Debug;
4147
session: Session;
4248
user: User;
4349
};

‎graphql/codegen.yml

+2
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ generates:
88
useIndexSignature: true
99
makeResolverTypeCallable: true
1010
contextType: "@acme/core#Context"
11+
defaultMapper: Partial<{T}>
1112
plugins:
1213
- typescript
1314
- typescript-resolvers
15+
1416
backend/services/apollo/schema.ts:
1517
plugins:
1618
- ./backend/codegen-typedef.js

‎graphql/debug.graphql

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
type Query {
2+
debug: Debug!
3+
}
4+
5+
type Debug {
6+
scrap: String!
7+
}

‎package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616
"@graphql-codegen/typescript-operations": "^2.2.2",
1717
"@graphql-codegen/typescript-resolvers": "^2.4.3",
1818
"@graphql-codegen/typescript-urql": "^3.4.2",
19-
"@serverless-stack/cli": "0.56.1",
20-
"@serverless-stack/resources": "0.56.1",
19+
"@serverless-stack/cli": "0.57.2",
20+
"@serverless-stack/resources": "0.57.2",
2121
"@tsconfig/node14": "^1.0.1",
2222
"graphql": "^16.2.0",
2323
"typescript": "^4.4.4"

‎stacks/Api.ts

+30-1
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,45 @@
11
import * as sst from "@serverless-stack/resources";
2+
import { HttpUserPoolAuthorizer } from "@aws-cdk/aws-apigatewayv2-authorizers";
3+
4+
import { Database } from "./Database";
5+
import { Auth } from "./Auth";
6+
7+
type Props = {
8+
db: Database["outputs"];
9+
auth: Auth["outputs"];
10+
};
211

312
export class Api extends sst.Stack {
413
public readonly outputs: {
514
apollo: string;
615
};
716

8-
constructor(scope: sst.App) {
17+
constructor(scope: sst.App, props: Props) {
918
super(scope, "api");
1019

1120
const apollo = new sst.ApolloApi(this, "apollo", {
1221
server: "services/apollo/apollo.handler",
22+
defaultAuthorizationType: sst.ApiAuthorizationType.JWT,
23+
defaultAuthorizer: new HttpUserPoolAuthorizer(
24+
"authorizer",
25+
props.auth.userPool,
26+
{
27+
userPoolClients: [props.auth.userPool.addClient("client")],
28+
}
29+
),
30+
defaultFunctionProps: {
31+
environment: {
32+
RDS_SECRET: props.db.cluster.secret!.secretArn,
33+
RDS_ARN: props.db.cluster.clusterArn,
34+
RDS_DATABASE: "postgres",
35+
},
36+
bundle: {
37+
nodeModules: ["kysely", "kysely-data-api"],
38+
},
39+
},
1340
});
41+
props.db.cluster.secret?.grantRead(apollo.serverFunction);
42+
props.db.cluster.grantDataApiAccess(apollo.serverFunction);
1443

1544
this.outputs = {
1645
apollo: apollo.url,

‎stacks/Auth.ts

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import * as sst from "@serverless-stack/resources";
2+
3+
export class Auth extends sst.Stack {
4+
public readonly outputs: {
5+
userPool: Exclude<sst.Auth["cognitoUserPool"], undefined>;
6+
};
7+
constructor(scope: sst.App) {
8+
super(scope, "auth");
9+
10+
const auth = new sst.Auth(this, "auth", {
11+
cognito: {
12+
userPool: {
13+
signInAliases: {
14+
email: true,
15+
},
16+
},
17+
},
18+
});
19+
20+
this.outputs = {
21+
userPool: auth.cognitoUserPool!,
22+
};
23+
}
24+
}

‎stacks/Database.ts

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import * as sst from "@serverless-stack/resources";
2+
import * as rds from "@aws-cdk/aws-rds";
3+
import * as ec2 from "@aws-cdk/aws-ec2";
4+
5+
export class Database extends sst.Stack {
6+
public readonly outputs: {
7+
cluster: rds.ServerlessCluster;
8+
};
9+
10+
constructor(scope: sst.App) {
11+
super(scope, "database");
12+
13+
// Create VPC solely for RDS as it is required. Nothing should be using this.
14+
const vpc = new ec2.Vpc(this, "vpc", {
15+
natGateways: 0,
16+
subnetConfiguration: [
17+
{
18+
cidrMask: 24,
19+
name: "public",
20+
subnetType: ec2.SubnetType.PUBLIC,
21+
},
22+
{
23+
cidrMask: 28,
24+
name: "rds",
25+
subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
26+
},
27+
],
28+
});
29+
30+
const cluster = new rds.ServerlessCluster(this, "cluster", {
31+
vpc,
32+
engine: rds.DatabaseClusterEngine.auroraPostgres({
33+
version: rds.AuroraPostgresEngineVersion.VER_10_14,
34+
}),
35+
enableDataApi: true,
36+
vpcSubnets: {
37+
subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
38+
},
39+
});
40+
41+
this.outputs = {
42+
cluster,
43+
};
44+
}
45+
}

‎stacks/index.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import * as sst from "@serverless-stack/resources";
22
import { Api } from "./Api";
3+
import { Auth } from "./Auth";
4+
import { Database } from "./Database";
35
import { Frontend } from "./Frontend";
46

57
export default function main(app: sst.App): void {
@@ -9,7 +11,12 @@ export default function main(app: sst.App): void {
911
environment: {},
1012
});
1113

12-
const api = new Api(app);
14+
const db = new Database(app);
15+
const auth = new Auth(app);
16+
const api = new Api(app, {
17+
db: db.outputs,
18+
auth: auth.outputs,
19+
});
1320
new Frontend(app, {
1421
api: api.outputs,
1522
});

0 commit comments

Comments
 (0)
Please sign in to comment.