-
Notifications
You must be signed in to change notification settings - Fork 267
/
Copy pathserver.js
166 lines (147 loc) · 5.11 KB
/
server.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
"use strict";
const express = require("express");
const mongoose = require("mongoose");
// Mongoose internally uses a promise-like object,
// but its better to make Mongoose use built in es6 promises
mongoose.Promise = global.Promise;
// config.js is where we control constants for entire
// app like PORT and DATABASE_URL
const { PORT, DATABASE_URL } = require("./config");
const { Restaurant } = require("./models");
const app = express();
app.use(express.json());
// GET requests to /restaurants => return 10 restaurants
app.get("/restaurants", (req, res) => {
Restaurant.find()
// we're limiting because restaurants db has > 25,000
// documents, and that's too much to process/return
.limit(10)
// success callback: for each restaurant we got back, we'll
// call the `.serialize` instance method we've created in
// models.js in order to only expose the data we want the API return.
.then(restaurants => {
res.json({
restaurants: restaurants.map(restaurant => restaurant.serialize())
});
})
.catch(err => {
console.error(err);
res.status(500).json({ message: "Internal server error" });
});
});
// can also request by ID
app.get("/restaurants/:id", (req, res) => {
Restaurant
// this is a convenience method Mongoose provides for searching
// by the object _id property
.findById(req.params.id)
.then(restaurant => res.json(restaurant.serialize()))
.catch(err => {
console.error(err);
res.status(500).json({ message: "Internal server error" });
});
});
app.post("/restaurants", (req, res) => {
const requiredFields = ["name", "borough", "cuisine"];
for (let i = 0; i < requiredFields.length; i++) {
const field = requiredFields[i];
if (!(field in req.body)) {
const message = `Missing \`${field}\` in request body`;
console.error(message);
return res.status(400).send(message);
}
}
Restaurant.create({
name: req.body.name,
borough: req.body.borough,
cuisine: req.body.cuisine,
grades: req.body.grades,
address: req.body.address
})
.then(restaurant => res.status(201).json(restaurant.serialize()))
.catch(err => {
console.error(err);
res.status(500).json({ message: "Internal server error" });
});
});
app.put("/restaurants/:id", (req, res) => {
// ensure that the id in the request path and the one in request body match
if (!(req.params.id && req.body.id && req.params.id === req.body.id)) {
const message =
`Request path id (${req.params.id}) and request body id ` +
`(${req.body.id}) must match`;
console.error(message);
return res.status(400).json({ message: message });
}
// we only support a subset of fields being updateable.
// if the user sent over any of the updatableFields, we udpate those values
// in document
const toUpdate = {};
const updateableFields = ["name", "borough", "cuisine", "address"];
updateableFields.forEach(field => {
if (field in req.body) {
toUpdate[field] = req.body[field];
}
});
Restaurant
// all key/value pairs in toUpdate will be updated -- that's what `$set` does
.findByIdAndUpdate(req.params.id, { $set: toUpdate })
.then(restaurant => res.status(204).end())
.catch(err => res.status(500).json({ message: "Internal server error" }));
});
app.delete("/restaurants/:id", (req, res) => {
Restaurant.findByIdAndRemove(req.params.id)
.then(restaurant => res.status(204).end())
.catch(err => res.status(500).json({ message: "Internal server error" }));
});
// catch-all endpoint if client makes request to non-existent endpoint
app.use("*", function(req, res) {
res.status(404).json({ message: "Not Found" });
});
// closeServer needs access to a server object, but that only
// gets created when `runServer` runs, so we declare `server` here
// and then assign a value to it in run
let server;
// this function connects to our database, then starts the server
function runServer(databaseUrl, port = PORT) {
return new Promise((resolve, reject) => {
mongoose.connect(
databaseUrl,
err => {
if (err) {
return reject(err);
}
server = app
.listen(port, () => {
console.log(`Your app is listening on port ${port}`);
resolve();
})
.on("error", err => {
mongoose.disconnect();
reject(err);
});
}
);
});
}
// this function closes the server, and returns a promise. we'll
// use it in our integration tests later.
function closeServer() {
return mongoose.disconnect().then(() => {
return new Promise((resolve, reject) => {
console.log("Closing server");
server.close(err => {
if (err) {
return reject(err);
}
resolve();
});
});
});
}
// if server.js is called directly (aka, with `node server.js`), this block
// runs. but we also export the runServer command so other code (for instance, test code) can start the server as needed.
if (require.main === module) {
runServer(DATABASE_URL).catch(err => console.error(err));
}
module.exports = { app, runServer, closeServer };