Creating the Express Application
Creating the Express Application
You are not supposed to write everything at once (maybe except for models, those are the first thing you should think about before you do any coding, and should be the first thing you ever set up in a backend application). Now we have written some controllers for our User entity, let’s test them out by building a test application.
Main setup
First we have to establish our database connection and configure our environment variables.
We will use MongoDB for our database. Setup your database according to this short video. Then, create a .env file in your backend directory:
1
2
3
MONGODB_URI={your_mongodb_url}
PORT=3001
SECRET_KEY=your_secret_jwt_key
NOTE: Never commit your
.envfile to version control! Add it to your.gitignorefile. Use jwt-keys.21no.de to generate a cryptographically strong secret string forSECRET_KEY.
Next, create a configuration file to handle environment variables:
1
2
3
4
5
6
7
8
9
10
11
12
import dotenv from 'dotenv';
dotenv.config();
const PORT = process.env.PORT || 3001;
const MONGODB_URI = process.env.MONGODB_URI || '';
const SECRET_KEY = process.env.SECRET_KEY || '';
export default {
PORT,
MONGODB_URI,
SECRET_KEY
};
Next, set up the routers for our endpoints. It makes the function we defined in the controller to be accessible in certain endpoints. For example:
1
2
3
4
5
6
7
8
9
import express from 'express';
import { getAll, getById } from '../controllers/userController';
const userRouter = express.Router();
userRouter.get('/', getAll);
userRouter.get('/:id', getById);
export default userRouter;
Similarly, let’s create a router for user registration:
1
2
3
4
5
6
7
8
import express from 'express';
import { register } from '../controllers/registerController';
const registerRouter = express.Router();
registerRouter.post('/', register);
export default registerRouter;
Next, create the main Express application file:
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
import express, { Request, Response } from 'express';
import mongoose from 'mongoose';
import config from './config';
import cors from 'cors';
import registerRouter from './routers/registerRouter';
import userRouter from './routers/userRouter';
const app = express();
// Enable CORS for frontend communication
app.use(cors());
// Connect to MongoDB
console.log("connecting to ", config.MONGODB_URI);
mongoose
.connect(config.MONGODB_URI)
.then(() => console.log("connected to MongoDB"))
.catch((error) =>
console.log("error connecting to MongoDB: ", error.message)
);
// Middleware for parsing JSON
app.use(express.json());
// Routes
app.use("/api/register", registerRouter);
app.use("/api/users", userRouter);
export default app;
These two lines
1
2
app.use("/api/register", registerRouter);
app.use("/api/users", userRouter);
are used to connect your routers. Think of it this way: you connect to the userRouter via the top domain /api/users. Then, to ask it to perform getById (refer to router setup part), we send a GET request to /api/users/{id}.
Finally, let’s create the entry point for our application:
1
2
3
4
5
6
import app from './app';
import config from './config';
app.listen(config.PORT, () => {
console.log(`Server running on port ${config.PORT}`);
});
Now our basic backend application should be done. First, configure your package.json file:
1
2
3
"scripts": {
"dev": "nodemon --watch src --exec \"ts-node -r tsconfig-paths/register\" src/index.ts"
}
The important part here is the -r tsconfig-paths/register part. This will enable path mapping support (like @shared/types) and without this your @shared/* imports won’t work. You can look up the rest if you don’t understand.
Then start your server:
1
2
cd backend
npm run dev
You should see the message “Server running on port 3001” and “connected to MongoDB”.
Testing
That was a lot of code. In order to check if our controllers are working properly, we have to test our controllers to see it is working as we expected. To do that we will use Postman. Watch this video for an introduction to Postman. After that, you should be able to test all of the methods below.
1. Register a New User
POST http://localhost:3001/api/register
Body (JSON):
1
2
3
4
5
6
{
"username": "johndoe",
"name": "John Doe",
"email": "john@example.com",
"password": "password123"
}
Expected Response (201 Created):
1
2
3
4
5
6
7
{
"username": "johndoe",
"name": "John Doe",
"email": "john@example.com",
"contacts": [],
"id": "60f7b3b3b3b3b3b3b3b3b3b3"
}
2. Get All Users
GET http://localhost:3001/api/users
Expected Response (200 OK):
1
2
3
4
5
6
7
8
9
[
{
"username": "johndoe",
"name": "John Doe",
"email": "john@example.com",
"contacts": [],
"id": "60f7b3b3b3b3b3b3b3b3b3b3"
}
]
3. Get User by ID
GET http://localhost:3001/api/users/{id}
(Replace the ID with the actual ID from your database)
Expected Response (200 OK):
1
2
3
4
5
6
7
{
"username": "johndoe",
"name": "John Doe",
"email": "john@example.com",
"contacts": [],
"id": "60f7b3b3b3b3b3b3b3b3b3b3"
}
Note: Notice that the
passwordHashfield is not included in the response. This is because of ourtoJSONtransformation in the User model that removes sensitive data.