Skip to content

Commit 381377d

Browse files
Tobias DittmannTobias Dittmann
authored andcommitted
First release
1 parent 26947e3 commit 381377d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+5234
-0
lines changed

.env.example

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# ------------------------------------------------
2+
# APPLICATION
3+
# ------------------------------------------------
4+
5+
# You can change the applications port here.
6+
# Defaults to 3000.
7+
# PORT=3000
8+
9+
# ------------------------------------------------
10+
# DATABASE
11+
# ------------------------------------------------
12+
13+
# Example connection strings:
14+
# mongodb://127.0.0.1:27017/rest-api-nodejs-mongodb
15+
# mongodb://[MongodbHost]:[PORT]/[DatabaseName]
16+
MONGODB_URL=ConnectionString
17+
18+
# ------------------------------------------------
19+
# JWT
20+
# ------------------------------------------------
21+
22+
# The secret may only container small letters and numbers (abcdefghijklmnopqrstuvwxyz1234567890).
23+
JWT_SECRET=YourSecret
24+
25+
# Further information about the syntax can be found in the
26+
# official github repository:
27+
# https://github.com/auth0/node-jsonwebtoken#usage
28+
JWT_TIMEOUT_DURATION="2 hours"
29+
30+
# ------------------------------------------------
31+
# EMAIL
32+
# ------------------------------------------------
33+
34+
# Connection to your smtp provider
35+
EMAIL_SMTP_HOST=smtp.example.dev
36+
EMAIL_SMTP_PORT=465
37+
EMAIL_SMTP_USERNAME=noreply
38+
EMAIL_SMTP_PASSWORD=noreply
39+
EMAIL_SMTP_SECURE=true # true for port 465, false for other ports.
40+
41+
# Sets the default 'from'-address
42+
43+
44+
# Sets the base url of links in emails, using the generateLink() method of utils.
45+
EMAIL_BASE_URL=http://localhost:3000

.gitignore

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Logs
2+
logs
3+
*.log
4+
npm-debug.log*
5+
yarn-debug.log*
6+
yarn-error.log*
7+
8+
# Runtime data
9+
pids
10+
*.pid
11+
*.seed
12+
*.pid.lock
13+
14+
# Directory for instrumented libs generated by jscoverage/JSCover
15+
lib-cov
16+
17+
# Coverage directory used by tools like istanbul
18+
coverage
19+
20+
# nyc test coverage
21+
.nyc_output
22+
23+
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24+
.grunt
25+
26+
# Bower dependency directory (https://bower.io/)
27+
bower_components
28+
29+
# node-waf configuration
30+
.lock-wscript
31+
32+
# Compiled binary addons (https://nodejs.org/api/addons.html)
33+
build/Release
34+
35+
# Dependency directories
36+
node_modules/
37+
jspm_packages/
38+
39+
# TypeScript v1 declaration files
40+
typings/
41+
42+
# Optional npm cache directory
43+
.npm
44+
45+
# Optional eslint cache
46+
.eslintcache
47+
48+
# Optional REPL history
49+
.node_repl_history
50+
51+
# Output of 'npm pack'
52+
*.tgz
53+
54+
# Yarn Integrity file
55+
.yarn-integrity
56+
57+
# dotenv environment variables file
58+
.env
59+
60+
# next.js build output
61+
.next
62+
63+
#vs code configurations
64+
.vscode/

README.md

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
# NodeJS Slender
2+
This is a barebone boilerplate for creating a nodejs api. It comes with the following features:
3+
- 🗂 [MongoDB](https://www.mongodb.com) intergation
4+
- 🔐 [JWT](https://jwt.io) Authentication
5+
- 📨 Templated emails using [MJML](https://mjml.io) and [Mustache](https://mustache.github.io) 🧔🏽
6+
- ✅ Request body validation using [Joi](https://hapi.dev/module/joi/)
7+
8+
---
9+
- [NodeJS Slender](#nodejs-slender)
10+
- [Creating a new project](#creating-a-new-project)
11+
- [Authentication](#authentication)
12+
- [Middleware](#middleware)
13+
- [Routes](#routes)
14+
- [Send emails from your app](#send-emails-from-your-app)
15+
- [Templated emails](#templated-emails)
16+
- [Simple emails](#simple-emails)
17+
- [Validation](#validation)
18+
19+
---
20+
21+
## Creating a new project
22+
For creating a new project you can just clone this repo and change the origin.
23+
```bash
24+
git clone [email protected]:txbivs/nodejs-slender.git <projectname> && cd <projectname>
25+
git remote set-url origin <your-repo>
26+
```
27+
28+
After that you need to setup the environment. Start by typing the following command in your terminal of choice:
29+
```bash
30+
cp .env.sample .env
31+
```
32+
That copies the sample `.env` file. Now you need to populate that with your own data.
33+
34+
Next you need to install the dependencies using your package manager of choice, I use `yarn`.
35+
```
36+
yarn install
37+
```
38+
39+
To start the app you need to type in one of the following commands depending on your current situation.
40+
```bash
41+
# Start app in development mode, with hot reloading enabled
42+
yarn dev
43+
44+
# Start app in production mode
45+
yarn start
46+
```
47+
48+
---
49+
50+
## Authentication
51+
The authentication is done using JWT. The generated token must be send with every following request in the `Authorization` header.
52+
53+
### Middleware
54+
To protect a route from unauthenticated users, you can add the `auth` middleware to it. To do that, you attach it to the `controller`'s export `array`. The last element of this `array` is the actual function. The elements before are used as middleware.
55+
56+
### Routes
57+
For the authentication the following routes are predefined and used:
58+
59+
- #### `POST /register`
60+
Creates a new user based on the incoming request body.
61+
62+
**Request**:
63+
- ##### `firstName`: `string`, `required` *The first name of the new user*
64+
- ##### `lastName`: `string`, `required` *The last name of the new user*
65+
- ##### `email`: `string`, `email`, `unique`, `required` *The users email. An email can only be registered once.*
66+
- ##### `password`: `string`, `min(6)`, `required` *The plaintext password. Will be hashed using bcrypt.*
67+
68+
**Response**: Responds with the created user. If failed -> returns errors.
69+
70+
##### Sample:
71+
```json
72+
body: {
73+
"firstName": "John",
74+
"lastName": "Doe",
75+
"email": "[email protected]",
76+
"password:": "!secret$"
77+
}
78+
```
79+
80+
- #### `POST /login`
81+
Attempts a login with the given credentials.
82+
83+
**Request**:
84+
- ##### `email`: `string`, `email`, `required` *The users email.*
85+
- ##### `password`: `string`, `required` *The plaintext password.*
86+
87+
**Response**: If valid -> returns the generated jwt token, if failed -> returns a `401
88+
Unauthorized` with a reason.
89+
90+
##### Sample:
91+
```json
92+
body: {
93+
"firstName": "John",
94+
"lastName": "Doe",
95+
"email": "[email protected]",
96+
"password:": "!secret$"
97+
}
98+
```
99+
100+
- #### `POST /verify`
101+
Attempts the confirmation of an account using the `confirmKey`.
102+
103+
**Request**:
104+
- ##### `confirmKey`: `string`, `length(16)`, `required` *The given 16 digit confirmation key*
105+
106+
**Response**: If valid -> returns the generated jwt token, if failed -> returns a `401
107+
Unauthorized` with a reason.
108+
109+
##### Sample:
110+
```json
111+
body: {
112+
"confirmKey": "ld84jflsow39lc0e"
113+
}
114+
```
115+
116+
---
117+
118+
## Send emails from your app
119+
There are two possible ways of sending an email. You can use mjml and mustache to generate beautiful, responsive emails with variables in a few lines of code, or just ship generic html or plaintext emails.
120+
121+
### Templated emails
122+
To use the built-in email functionality you have to add your templates first. Add your template as `<name>.mjml` to the `mail/template` folder.
123+
You can create subfolders in the `mail/template` folder for organization, but keep in mind that you need to append that subfolder name to the path
124+
to the template when attempting to render it!
125+
126+
Let's assume you created a new template under `mail/template/monitoring/ping.mjml`. For sending an email the only
127+
code you need is the following:
128+
```javascript
129+
const Mail = require('path/to/mail/Mail');
130+
131+
// Create a new Mail instance by giving it the receipient and a subject.
132+
let mail = new Mail('[email protected]', 'Monitoring Alert');
133+
134+
// Render the variables by specifying the template and an object with the variable data.
135+
// The sample assumes you have used the variables {{ url }} and {{ downtime }} in your mjml-template.
136+
mail.render('monitoring/ping', {
137+
host: '127.0.0.1',
138+
downtime: '20m'
139+
});
140+
141+
mail.send();
142+
```
143+
144+
The `send()` function will return a `Promise`. So if you want to do something with failed emails, like logging or resending them after 1 day, or wait for the email to be sent, you can use the following code:
145+
146+
```javascript
147+
mail.send().then(() => {
148+
// Do something on success.
149+
}).catch((err) => {
150+
console.error(err);
151+
// Do something with the failed email.
152+
});
153+
```
154+
155+
### Simple emails
156+
If you just want to send a basic email without templating and mjml, you can use the `mail/mailer` module. Import
157+
it and use the send function.
158+
```javascript
159+
const mailer = require('path/to/mail/mailer');
160+
161+
mailer.send(_to, _subject, _htmlOrText, _from);
162+
```
163+
This function is returing a `Promise` too.
164+
165+
---
166+
167+
## Validation
168+
Validation is super easy with this boilerplate. All the validation rules are stored in the `validation/rules` module.
169+
You can create your own files, but make sure you require them in where you need them. These rules are basicly just objects consumed by Joi. To enable validation for a new route, add the route segments to the `validation/rules` module and add the `validator` middleware to your desired route.
170+
171+
For example you want to enable validation for the route `POST /posts` you would create an object under `posts.create` with your desired validation rules. After that, go into your `PostController` and add the following import:
172+
```javascript
173+
const { posts } = require('path/to/validation/rules');
174+
```
175+
and add the middleware to your method:
176+
```javascript
177+
exports.create = [
178+
auth,
179+
validator(posts.create),
180+
(req, res) => {
181+
// Your controller logic
182+
}
183+
];
184+
```

app.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
const express = require("express");
2+
const path = require("path");
3+
const logger = require("morgan");
4+
require("dotenv").config();
5+
const router = require("./route/router");
6+
const RestResponse = require('./app/response/RestResponse');
7+
const cors = require("cors");
8+
9+
// DB connection
10+
const MONGODB_URL = process.env.MONGODB_URL;
11+
const mongoose = require("mongoose");
12+
mongoose.connect(MONGODB_URL, { useNewUrlParser: true, useUnifiedTopology: true }).then(() => {
13+
//don't show the log when it is test
14+
if(process.env.NODE_ENV !== "test") {
15+
console.log("Connected to %s", MONGODB_URL);
16+
console.log("App is running ... \n");
17+
console.log("Press CTRL + C to stop the process. \n");
18+
}
19+
})
20+
.catch(err => {
21+
console.error("App starting error:", err.message);
22+
process.exit(1);
23+
});
24+
const db = mongoose.connection;
25+
26+
let app = express();
27+
28+
//don't show the log when it is test
29+
if(process.env.NODE_ENV !== "test") {
30+
app.use(logger("dev"));
31+
}
32+
app.use(express.json());
33+
app.use(express.urlencoded({ extended: false }));
34+
35+
//To allow cross-origin requests
36+
app.use(cors());
37+
38+
app.use("/", router);
39+
40+
// throw 404 if URL not found
41+
app.all("*", function(req, res) {
42+
return RestResponse.notFound(res);
43+
});
44+
45+
app.use((err, req, res) => {
46+
if(err.name == "UnauthorizedError"){
47+
return RestResponse.unauthorized(res, err.message);
48+
}
49+
});
50+
51+
module.exports = app;

0 commit comments

Comments
 (0)