Skip to content

Commit a4e1032

Browse files
authored
Add graphql support to node backend (#17)
* add datasources take apollo apis as the datasources * add schema, resolvers to graphql * add field and ignoreFile for graphql in zero-module.yml * remove store.sqlite from git * switch sqlite to default database enable automatic table creating for graphql testing * update file apis to match Go version * add graphql for s3 file apis * change type to category due to type is against preserve keyword type * Strip down graphql functions * change expression 'graphql=yes/no' to apiType='graphql/rest' * add apiType="graphql/rest" to package.json * support users to choose buckets * Strip down graphql functions * add graphql for authAPI * add env variable GRAPHQL_BASE_URL to Kubernetes config * imitate middleware/auth/index.js to add authentication to graphql * restructure directories for graphql and rest api * refactor file apis with FileService that is used by graphql as well * add trip service for demonstration * remove all datasources from graphql and remove initdb.js * add mockauth and standard jwt decode middleware * add standard jwtDecoder in middleware * seperate main entry for rest and graphql * optimize variable definition pre-keyword * remove env var GRAPHQL_BASE_URL from kustomization.yml * add documentation to graphql schema * remove health check endpoints through Graphql seperate example trip schema and resolver from the default ones * update documnetation for graphql schemas removed the original schema.js, resolvers and User.js * update ignorefile for rest apipType * change download link from s3 presigned url to cloudfront signed url. * update auth component and add the whitelist filter * remove the empty template block * split persignedurls to downloadSignedUrl and uploadSignedUrl rename auth_whitelist to authAllowlist
1 parent b9bce6e commit a4e1032

File tree

18 files changed

+388
-93
lines changed

18 files changed

+388
-93
lines changed

templates/kubernetes/overlays/staging/kustomization.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,4 @@ configMapGenerator:
1717
literals:
1818
- ENVIRONMENT=staging
1919
- DOMAIN=<% index .Params `stagingHostRoot` %>
20-
- S3_BUCKET=files.<% index .Params `stagingHostRoot` %>
20+
- S3_BUCKET=files.<% index .Params `stagingHostRoot` %>

templates/package.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"engines": {
66
"node": "12"
77
},
8-
"main": "src/app.js",
8+
"main": <%if eq (index .Params `apiType`) "rest" %>"src/app.js", <% else %>"src/graphql.js",<% end %>
99
"scripts": {
1010
"test": "echo \"Error: no test specified\" && exit 1",
1111
"lint": "eslint src",
@@ -15,6 +15,13 @@
1515
"author": "",
1616
"license": "ISC",
1717
"dependencies": {
18+
<%if eq (index .Params `apiType`) "graphql" %>
19+
"apollo-datasource": "^0.7.3",
20+
"apollo-datasource-rest": "^0.9.7",
21+
"apollo-server-express": "^2.19.2",
22+
"graphql": "^15.5.0",
23+
"graphql-combine": "^1.0.1",
24+
<% end %>
1825
"aws-cloudfront-sign": "^2.2.0",
1926
"aws-sdk": "^2.744.0",
2027
"dotenv": "^8.2.0",

templates/src/app.js

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,38 @@
1-
var dotenv = require("dotenv");
2-
var express = require("express");
3-
var morgan = require("morgan");
4-
5-
var { connect } = require("./db");
1+
const dotenv = require("dotenv");
2+
const express = require("express");
3+
const morgan = require("morgan");
4+
const dbDatasource = require("./db");
5+
<%if eq (index .Params `fileUploads`) "yes" %>const fileRoutes = require("./app/file");<% end %>
6+
<%if eq (index .Params `userAuth`) "yes" %>const authRoutes = require("./app/auth");
7+
const { authMiddleware } = require("./middleware/auth");<% end %>
68
const statusRoutes = require("./app/status");
7-
<%if eq (index .Params `fileUploads`) "yes" %>const fileRoutes = require("./app/file");
8-
<% end %><%if eq (index .Params `userAuth`) "yes" %>const authRoutes = require("./app/auth");
9-
<% end %>
9+
1010
dotenv.config();
11-
var app = express();
11+
const app = express();
1212
app.use(morgan("combined"));
13+
app.use(express.json());
14+
app.use(express.urlencoded({ extended: true }));
15+
16+
<%if eq (index .Params `userAuth`) "yes" %>app.use(authMiddleware);
17+
app.use("/auth", authRoutes);<% end %>
18+
19+
<%if eq (index .Params `fileUploads`) "yes" %>app.use("/file", fileRoutes);<% end %>
1320
1421
app.use("/status", statusRoutes);
15-
<%if eq (index .Params `userAuth`) "yes" %>app.use("/auth", authRoutes);
16-
<% end %><%if eq (index .Params `fileUploads`) "yes" %>app.use("/file", fileRoutes);
17-
<% end %>
18-
var port = process.env.SERVER_PORT;
22+
23+
const port = process.env.SERVER_PORT;
1924
if (!port) {
2025
port = 3000;
2126
}
2227
23-
async function main() {
24-
const database = await connect();
25-
28+
const main = async () => {
2629
// remove this block for development, just for verifying DB
2730
try {
28-
const res = await database.query("SELECT 1");
31+
await dbDatasource.authenticate();
32+
console.log("Connection has been established successfully.");
33+
await dbDatasource.sync( {alter: true} );
34+
console.log("Created or altered tables");
35+
const res = await dbDatasource.query("SELECT 1");
2936
console.log(`Query successful, returned ${res[0].length} rows.`);
3037
} catch (e) {
3138
console.error(e);
@@ -34,6 +41,7 @@ async function main() {
3441
app.listen(port, () => {
3542
console.log(`Example app listening at http://localhost:${port}`);
3643
});
37-
}
44+
};
3845

3946
main();
47+

templates/src/app/auth/index.js

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
var { Router } = require("express");
1+
const { Router } = require("express");
22

3-
var { authMiddleware } = require("../../middleware/auth");
3+
const router = Router();
44

5-
var router = Router()
6-
7-
router.get("/userInfo", authMiddleware, (req, res) => {
5+
router.get("/userInfo", (req, res) => {
86
res.json(req.user);
97
});
108

templates/src/app/file/index.js

Lines changed: 10 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,17 @@
1-
var { Router } = require("express");
2-
var aws = require("aws-sdk");
3-
var cfsign = require("aws-cloudfront-sign");
1+
const { Router } = require("express");
2+
const FileService = require("../../service/file");
43

5-
var router = Router()
6-
var s3 = new aws.S3();
4+
const router = Router();
5+
const fileService = new FileService();
76

8-
router.get("/presigned/:key", (req, res) => {
9-
var params = {
10-
Bucket: process.env.S3_BUCKET,
11-
Fields: {
12-
key: req.params.key,
13-
},
14-
};
15-
16-
s3.createPresignedPost(params, (err, data) => {
17-
if (err) {
18-
console.error(err);
19-
res.sendStatus(500);
20-
} else {
21-
console.log(data);
22-
res.send(data);
23-
}
24-
});
7+
router.get("/presigned", (req, res) => {
8+
let key = req.query.key;
9+
return res.json(fileService.getUploadSignedUrl( key ));
2510
});
2611

27-
router.get("/:key", (req, res) => {
28-
var params = {
29-
keypairId: process.env.CF_KEYPAIR_ID,
30-
privateKeyString: process.env.CF_KEYPAIR_SECRET_KEY,
31-
expireTime: new Date().getTime() + 30000, // defaults to 30s
32-
};
33-
34-
var url = cfsign.getSignedUrl(
35-
`https://files.${process.env.DOMAIN}/${req.params.key}`,
36-
params
37-
);
38-
39-
console.log(url);
40-
res.redirect(url);
12+
router.get("/",(req, res) => {
13+
let key = req.query.key;
14+
return res.json(fileService.getDownloadSignedUrl( key ));
4115
});
4216

4317
module.exports = router;

templates/src/app/status/index.js

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
1-
var { Router } = require("express");
1+
const { Router } = require("express");
22

3-
var router = Router()
3+
const router = Router();
44

55
router.get("/ready", (req, res) => {
6-
res.send("OK");
6+
res.json({ ready: "OK" });
77
});
88

99
router.get("/alive", (req, res) => {
10-
res.send("OK");
10+
res.json( {alive: "OK"} );
1111
});
1212

1313
router.get("/about", (req, res) => {
14-
res.send({
15-
podName: process.env.POD_NAME,
14+
var podName = (process.env.POD_NAME)?process.env.POD_NAME:"zero-node-backend";
15+
res.json({
16+
podName: podName,
1617
});
1718
});
1819

templates/src/conf/index.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
const conf = {
2+
jwtSecret: "SecretfromIdentityProvider",
3+
s3PresignedExpires: 60 * 5, //5 minutes
4+
cfSignerExpires: 1000 * 60 * 5 //5 minutes
5+
}
6+
7+
module.exports = conf;

templates/src/db/index.js

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const {
1111
DATABASE_NAME,
1212
} = process.env;
1313

14-
const database = new Sequelize(
14+
const datasource = new Sequelize(
1515
DATABASE_NAME,
1616
DATABASE_USERNAME,
1717
DATABASE_PASSWORD,
@@ -22,16 +22,4 @@ const database = new Sequelize(
2222
}
2323
);
2424

25-
const connect = async () => {
26-
try {
27-
await database.authenticate();
28-
console.log("Connection has been established successfully.");
29-
return database;
30-
} catch (error) {
31-
console.error("Unable to connect to the database:", error);
32-
}
33-
};
34-
35-
module.exports = {
36-
connect,
37-
};
25+
module.exports = datasource;

templates/src/db/model/Trip.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
const {Model, DataTypes} = require("sequelize");
2+
const datasource = require("../index");
3+
4+
class Trip extends Model{}
5+
6+
Trip.init({
7+
id: {
8+
type: DataTypes.INTEGER,
9+
primaryKey: true,
10+
autoIncrement: true,
11+
},
12+
createdAt: DataTypes.DATE,
13+
updatedAt: DataTypes.DATE,
14+
launchId: DataTypes.INTEGER,
15+
userId: DataTypes.INTEGER,
16+
},{
17+
sequelize: datasource,
18+
tableName: "trip"
19+
});
20+
21+
module.exports = Trip;

templates/src/graphql.js

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
const dotenv = require("dotenv");
2+
const express = require("express");
3+
const morgan = require("morgan");
4+
const { ApolloServer } = require("apollo-server-express");
5+
6+
const combine = require("graphql-combine");
7+
const path = require("path");
8+
const dbDatasource = require("./db");
9+
<%if eq (index .Params `userAuth`) "yes" %>const { authMiddleware } = require("./middleware/auth");<% end %>
10+
11+
dotenv.config();
12+
const app = express();
13+
app.use(morgan("combined"));
14+
app.use(express.json());
15+
app.use(express.urlencoded({ extended: true }));
16+
17+
app.use(authMiddleware);
18+
19+
const {typeDefs, resolvers} = combine({
20+
typeDefs: path.join(__dirname, "graphql/*.graphql"),
21+
resolvers: path.join(__dirname, "graphql/*_resolver.js")
22+
});
23+
24+
const server = new ApolloServer({
25+
context: async ( {req} ) => {
26+
if(req.user){
27+
return { user: {id: req.user.id, email: req.user.email} };
28+
}
29+
},
30+
typeDefs,
31+
resolvers
32+
});
33+
server.applyMiddleware({ app });
34+
35+
const port = process.env.SERVER_PORT;
36+
if (!port) {
37+
port = 3000;
38+
}
39+
40+
const main = async () => {
41+
// remove this block for development, just for verifying DB
42+
try {
43+
await dbDatasource.authenticate();
44+
console.log("Connection has been established successfully.");
45+
await dbDatasource.sync( {alter: true} );
46+
console.log("Created or altered tables");
47+
const res = await dbDatasource.query("SELECT 1");
48+
console.log(`Query successful, returned ${res[0].length} rows.`);
49+
} catch (e) {
50+
console.error(e);
51+
}
52+
53+
app.listen(port, () => {
54+
console.log(`Example app listening at http://localhost:${port}`);
55+
});
56+
};
57+
58+
main();
59+

0 commit comments

Comments
 (0)