How to Full Stack JavaScript
By: Andrew Bliss
Tags: Nodejs, React, JavaScript, Docker, Docker Compose

Api Server

This Api will be running ExpressJs, and we will be using a JWT for authentication.

/**

* JWT Authentication

*/

app.use(

expressjwt({

algorithms: ['HS256'],

secret: process.env.JWT_SECRET,

credentialsRequired: false,

getToken(req) {

if (req.headers.authorization && req.headers.authorization.split(' ')[0] === 'Bearer') {

return req.headers.authorization.split(' ')[1];

} else if (req.query && req.query.token) {

return req.query.token;

}

return null;

},

})

);

This will take our JWT_SECRET environment variable and setup our authenticate. It will then look for the token in the headers or query string.

Then let’s start our server up listening on our PORT environment variable.

const server = app.listen(process.env.PORT, () => {

console.log(

`

Express Server started.

Port: ${app.get('port')}

`

);

});

Let’s setup a login route so users can login.

import express, { Request, Response, NextFunction } from 'express';

import { asyncEndpoint, validateSchema } from '../../../../../../middleware';

import joi from '@hapi/joi'; const router = express.Router(); const loginSchema = {

path: 'body',

schema: joi.object().keys({

email: joi.string().required(),

password: joi.string().required(),

}),

}; const login = async (req: Request, res: Response, next: NextFunction) => {

const { Users } = req.app.get('db');

const user = await Users.findOne({

attributes: ['id', 'accountId', 'email', 'password'],

where: {

email: req.body.email,

},

}); if (!user || !user.verifyPassword(req.body.password)) {

throw {

status: 403,

message: 'Email or Password is incorrect',

};

}



req.refreshJWT(user.id, user.accountId);

res.end();

}; router.post('/', validateSchema(loginSchema), asyncEndpoint(login)); export default router;

In this file we will bring in some data validation from the @hapi/joi package. We will validate to only allow input of an email and password. Then we will check the database and password to see if there are correct.

Now that we have our endpoint to login to the system we can write a login component in React.

import React, { useState } from 'react';

import { useObserver } from 'mobx-react-lite';

import { useAuth } from '../store/Context';

import Button from '@material-ui/core/Button';

import TextField from '@material-ui/core/TextField';

import Dialog from '@material-ui/core/Dialog';

import DialogActions from '@material-ui/core/DialogActions';

import DialogContent from '@material-ui/core/DialogContent';

import DialogContentText from '@material-ui/core/DialogContentText';

import DialogTitle from '@material-ui/core/DialogTitle';

import useSnackbar from 'hooks/useSnackbar';

import Slide from '@material-ui/core/Slide';

import { TransitionProps } from '@material-ui/core/transitions';

import Api from 'services/Api'; const Transition = React.forwardRef(function Transition(

props: TransitionProps & { children?: React.ReactElement<any, any> },

ref: React.Ref<unknown>) {

return <Slide direction="up" ref={ref} {...props} />;

}); const Login: React.FC = () => {

const auth: any = useAuth();

const [email, setEmail] = useState('');

const [password, setPassword] = useState('');

const handleClose = () => {

auth.setOpenLoginDialog(false);

};

const handleRegister = () => {

auth.setOpenLoginDialog(false);

auth.setOpenRegisterDialog(true);

};

const handleForgotPassword = () => {

auth.setOpenLoginDialog(false);

auth.setOpenRegisterDialog(false);

auth.setOpenForgotPasswordDialog(true);

};

const handleSubmit = useSnackbar(async (snackbar: any) => {

await Api.post('/api/v1/login', {

email,

password,

});

window.location.href = '/dashboard';

});

return useObserver(() => {

return (

<Dialog

open={auth.openLoginDialog}

TransitionComponent={Transition}

onClose={handleClose}

aria-labelledby="login"

>

<DialogTitle id="login">Login</DialogTitle>

<DialogContent>

<DialogContentText>

To login to this website, please enter your email address and

password here.

</DialogContentText>

<TextField

autoFocus

margin="dense"

id="email"

label="Email Address"

type="email"

fullWidth

value={email}

onChange={(e: any) => setEmail(e.target.value)} />

<TextField

autoFocus

margin="dense"

id="password"

label="Password"

type="password"

fullWidth

value={password}

onChange={(e: any) => setPassword(e.target.value)} />

</DialogContent>

<DialogActions>

<Button onClick={handleClose} color="secondary"> Cancel

</Button>

<Button onClick={handleForgotPassword} color="primary"> Forgot Password

</Button>

<Button onClick={handleRegister} color="primary"> Register

</Button>

<Button

onClick={handleSubmit}

color="primary"

disabled={!email || !password}

>

Login

</Button>

</DialogActions>

</Dialog>

);

});

}; export default Login;

This component uses a Mobx store to handle state of the email and password. When the user clicks the Login button it will fire off the handleSubmit function and calls our backend Api.