Creating a Restfull Server Using NextJs

Description

  • NextJs will automatically create routes for all of the files within the pages\api directory.
  • In this example, a file called pages\api\products.js has been created that will return a JSON list of products.
  • pages\index.js` demonstrates how to use the fetch function to get the product data from the api.

Getting Started

  • Install the packages npm install
  • Run the server npm dev run
  • View the products code in the file pages\api\products.js
  • Navigate to the page http:\localhost:3000\api\products
  • or using Postman issue an GET request to the URL http:\\localhost:3000\api\products
  • The result will be a list of products

    json
    [
    "apple",
    "orange",
    "pear"
    ]

  • Navigate to the page http:\localhost:3000

How To Create a Next JS App From Scratch

Procedure

  • Create a directory for the app
  • Create a sub-directory called pages
  • Create a file called index.js in the pages directory.
  • Install next npm install next
  • Install react npm install react react-dom
  • Add the scripts to packages.json using the code

    json
    "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start"
    },

How To Build A Static Page That Refreshes Every 60 Seconds Using NextJs

In this example, we will build a page that is rendered server-side using NextJs. NextJs will be configured to re-render the page every 60 seconds.

Description

  • The code for generating the server-side content is in the function export async function getStaticProps(){
  • The page will refresh every 60 seconds because the return statement contain the revalidate 60. return {props: {productsFromDb: productsFromDb}, revalidate: 60}

Getting Started

  • Install package npm install
  • Build the site npm run build
  • Start the site npm run start

How To Mock A Function That Returns A Value

In this article we are demostrate how to mock a function that returns a value.

Procedure

  • Create the react app npc create-react-app demo-mocking-return-value.
  • Change directory cd demo-mocking-return-value
  • Create the file DemoMockReturnValue.js in the src directory. A referance to the function f is passed to the component as a prop. The function f is called and the result from the function is used to update the div with the test id myresult.
    DemoMockReturnValue.js

  • Create the file DemoMockReturnValue.test.js in the src directory. The unit test will create a mocked function f using the jest library const f = jest.fn();. The function is confirmed to return a value using the code f.mockReturnValue(expectedValue);. The assert confirms that the div with the test id myresult contains the expected data.
    DemoMockReturnValue.test.js

  • Run the tests npm test

How To Mock A Function And Confirm the Function Was Called

In this article we are demostrate how to mock a function and confirm the function was called multiple times with two different tests of arguments.

Procedure

  • Create the react app npc create-react-app demo-mocking-function-calls.
  • Change directory cd demo-mocking-function-calls
  • Create the file DemoMockingFunctionCalls.js in the src directory. A referance to the function f is passed as an argument to the function DemoMockingFunctionCalls. The function calls f twice with two different sets of arguments.
    DemoMockingFunctionCalls.js

  • Create the file DemoMockingFunctionCalls.test.js in the src directory. The unit test will create a mocked function f using the jest library const f = jest.fn();. The assert checks that the function was called twice by checking the f.mock.calls matches the array of expected calls.
    DemoMockingFunctionCalls.test.js

  • Run the tests npm test

How To Create a React Component That Returns a Promise With Unit Tests

In this article we will create a React component that returns a Promise. The promise will be the sum of two numbers. We will also create unit test to confirm the components functionality.

Component Setup

  • create the app npx create-react-app demo-promise.
  • Change directory cd demo-promise.
  • Create a directory within src called components.
  • Create a file called DemoPromise.js withing the components directory.

const DemoPromise = ({a,b}) => {
    return Promise.resolve(a+b);
}

export default DemoPromise;
  • Create a file called DemoPromise.test.js withing the components directory.
import DemoPromise from "./DemoPromise"

test('Should confirm promise returns sum of a and b', async () => {
    // Arrange
    const a = 1;
    const b = 2;

    // Act
    const result = await DemoPromise({a,b});

    // Assert
    expect(result).toBe(a + b);
})
  • Execute the tests npm test

Validation UK Phone Numbers With Python and Regular Expressions

To validate phone numbers, we need to consider the common formats for UK phone numbers. Generally, UK phone numbers have the following characteristics:

Landline numbers typically start with ’01’, ’02’, ’03’, or ‘084’, followed by 9 or 10 digits.
Mobile numbers start with ’07’ and are followed by 9 digits.
We’ll use regular expressions (regex) to match these patterns. Let’s write the function and the unit tests.

Phone Number Validation Function

import re

def is_valid_uk_phone_number(phone_number: str) -> bool:
    # Define the regex pattern for UK phone numbers allowing spaces
    pattern = re.compile(r'^(?:0|\+?44)\s?(\d\s?){9,10}$')
    return bool(pattern.match(phone_number))

Unit Tests

import unittest
from UKPhoneNumberValidation import *

class TestUKPhoneNumberValidation(unittest.TestCase):

    def test_valid_landline_numbers(self):
        self.assertTrue(is_valid_uk_phone_number("01234567890"))
        self.assertTrue(is_valid_uk_phone_number("020 7946 0123"))
        self.assertTrue(is_valid_uk_phone_number("02079460123"))
        self.assertTrue(is_valid_uk_phone_number("+44 1234 567890"))
        self.assertTrue(is_valid_uk_phone_number("+441234567890"))
        self.assertTrue(is_valid_uk_phone_number("01625 611979"))

    def test_valid_mobile_numbers(self):
        self.assertTrue(is_valid_uk_phone_number("07123456789"))
        self.assertTrue(is_valid_uk_phone_number("07 123 456 789"))
        self.assertTrue(is_valid_uk_phone_number("+44 7123 456789"))
        self.assertTrue(is_valid_uk_phone_number("+447123456789"))

    def test_invalid_phone_numbers(self):
        self.assertFalse(is_valid_uk_phone_number("1234567890"))  # Missing leading zero or +44
        self.assertFalse(is_valid_uk_phone_number("07890 1234567"))  # Too many digits
        self.assertFalse(is_valid_uk_phone_number("07890 1234"))  # Too few digits
        self.assertFalse(is_valid_uk_phone_number("0800 123 45"))  # Too few digits for landline

    def test_non_numeric_characters(self):
        self.assertFalse(is_valid_uk_phone_number("07a23456789"))  # Alphabetic character
        self.assertFalse(is_valid_uk_phone_number("07-23456789"))  # Hyphen

if __name__ == "__main__":
    unittest.main()

How to Handle Server-side API Routes in React

Introduction

In a React app, users can inspect the page’s HTML and read the JavaScript code, though with some limitations.

HTML

React generates HTML elements dynamically in the browser based on the current state of the app. Users can inspect the final HTML that React renders by using browser developer tools (like Chrome DevTools). This HTML will not directly show React components, but rather the output DOM elements (e.g., div, button, etc.) that React generates.

JavaScript

JavaScript Code: The JavaScript code from a React app is usually bundled (with tools like Webpack or Vite) and minified for production. Even though it is bundled and minified, users can still access the JavaScript by inspecting the source files in the browser developer tools. However, the code might be harder to read because minification and bundling obfuscate variable names and split the code across many files.

React Components: The original component code (like JSX syntax) isn’t visible to users. Instead, they will see the compiled JavaScript that React produces after the build process. Although, with proper tooling or reverse engineering, users can understand the functionality of the app.

To protect sensitive logic, it’s important not to include secrets or critical business logic on the client-side JavaScript, as users can always access client-side code. Sensitive operations should always be handled server-side.

Hiding Secrets Within Routes

It is possible to handle server-side API routes within a React project, especially if you are using Next.js, which is a React framework that allows you to write server-side code alongside your client-side code. In Next.js, API routes are server-side endpoints defined within the app that can be used to handle things like form submissions, API requests, and more. These API routes are hidden from the client since they run server-side.

Here’s how you can hide a POST request in Next.js inside a route like pages/api/chat.js.

1. Setup a Next.js App

First, create a new Next.js app

npx create-next-app@latest my-nextjs-app
cd my-nextjs-app

2. Create the API Route

In Next.js, you can create API routes by placing files in the /pages/api directory. These files will automatically be treated as server-side routes.

Here’s an example where we create an API route for /api/chat that accepts POST requests. This route will only respond to POST requests. The route accepts a text parameter from the request body, processes it, and returns a JSON response.

Create a new file at /pages/api/chat.js (API Route in Next.js)

export default async function handler(req, res) {
  if (req.method === 'POST') {
    const { text } = req.body;

    if (text) {
      // Simulate processing the request
      // TODO: Add secure post request here
      res.status(200).json({ message: `Server received: ${text}` });
    } else {
      res.status(400).json({ error: 'Text is required' });
    }
  } else {
    // Handle any other HTTP method
    res.setHeader('Allow', ['POST']);
    res.status(405).end(`Method ${req.method} Not Allowed`);
  }
}

3. Create the Frontend Form

In your Next.js app, create a form in the main page to send a POST request to the /api/chat endpoint.

Create a new file at /pages/index.js (Frontend Form)

import { useState } from 'react';

export default function Home() {
  const [text, setText] = useState('');
  const [response, setResponse] = useState('');

  const handleSubmit = async () => {
    try {
      const res = await fetch('/api/chat', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ text }),
      });

      const data = await res.json();
      setResponse(data.message || data.error);
    } catch (error) {
      setResponse('An error occurred.');
    }
  };

  return (
    <div style={{ padding: '2rem' }}>
      <h1>Send a Message</h1>
      <textarea
        value={text}
        onChange={(e) => setText(e.target.value)}
        placeholder="Enter your message here"
        rows="4"
        cols="50"
      />
      <br />
      <button onClick={handleSubmit}>Send</button>
      {response && <p>Response: {response}</p>}
    </div>
  );
}

4. Remove app/pages.js

To avoid a conflict between /pages/index.js and /app/pages.js. Delete /app/pages.js file.

5. Run the Next.js App

Now, run your Next.js app

npm run dev

The app will be available at http://localhost:3000.

The application will ask for a message

app1

Enter a message and then click the submit button.

After clicking the sumbit button, the app will make a request to the backend pages/api/chat.js, the following code from chat.js simulates making a secure post request and responds with the “server received”.

if (text) {
// Simulate processing the request
// TODO: Add secure post request here
res.status(200).json({ message: `Server received: ${text}` });

app2

Explanation

Backend (API Route): The API route /api/chat defined in pages/api/chat.js runs on the server side. When the frontend makes a request to /api/chat, it triggers the server-side code to process the request and send back a response.

Frontend (Form): The form in pages/index.js allows the user to enter some text. When the user clicks “Send”, it sends the text to the server-side API route using the fetch API, and the response is displayed on the page.

Why This is Secure

Server-Side API Route: The server-side code (/pages/api/chat.js) runs in Node.js on the server, and it is never exposed to the client. This is because all code inside /pages/api is only executed on the server.

Separation of Concerns: The client (React components) only knows about the API route, not the server-side implementation. This ensures that any sensitive logic or data processing is hidden from the client-side.

Folder Structure:

my-nextjs-app
├── pages
│   ├── api
│   │   └── chat.js      # API route for handling POST requests
│   └── index.js         # Frontend form
├── public
├── styles
├── package.json
├── next.config.js

With this approach, you have successfully hidden your server-side logic inside the API route in Next.js, which ensures that sensitive operations like POST requests and form submissions are handled securely and aren’t visible to users on the client side.

How To Create a Combobox React Component With Unit Tests

In this article we will create a React component that displays a Combobox. The values of the combobox are set from an array. We will also create unit test to confirm the components functionality.

Component Setup

  • Create the app npx create-react-app demo-select.
  • Change directory cd demo-select.
  • Create a directory within src called components.
  • Create a file called DemoSelect.js withing the components directory.
    DemoSelect.js

  • Create a file called DemoSelect.test.js
    DemoSelect.test.js

  • Run the tests npm test

  • To use the component add an include line to your project file eg import DemoSelect from './DemoSelect';. Create a list of options eg const optionList = ["apple", "orange"];, create an instance of the component and pass the list to the component <DemoSelect optionList={optionList} />.

How To Unit Test a Flask Restful Post Endpoint Using Python

In this article, we are going to explore how to unit test a simple POST restful endpoint using Python 3 and the modules Flask, Flask_restx, and UnitTest.

Creating The Application

First, we are going to create a simple flask application, the application will host a POST endpoint called /Setting that will allow for a new setting value to be posted.

  • Create a text file called endPointPost.py and add the following code.
from flask import Flask
from flask_restx import Resource, Api, reqparse
from flask_cors import CORS

app = Flask(__name__)
CORS(app)  # Allow CORS for all routes
api = Api(app, version='1.0', title='EndPoint Get', description='Example Restful Service')

SettingName = 'Name'
SettingValue = 'Value'

parserAdd = reqparse.RequestParser()
parserAdd.add_argument(SettingName, type=str, help='Setting Name', required=True)
parserAdd.add_argument(SettingValue, type=str, help='Setting Value', required=True)

mySettingsData = [
            {'Name': 'Setting1', 'Value': 'Value1'},
            {'Name': 'Setting2', 'Value': 'Value2'}
        ]

def getSettingValue(sname):
    for setting in mySettingsData:
        if setting[SettingName] == sname:
            return setting[SettingValue]
    return None

@api.route('/Settings', doc={"description": "Get all settings"})
class GetSettings(Resource):
    def get(self):
        return mySettingsData

@api.route('/Setting', doc={"description": "Add a new setting"})
class AddSetting(Resource):
    @api.doc(parser=parserAdd)
    def post(self):
        args = parserAdd.parse_args()
        sname = args[SettingName]
        svalue = args[SettingValue]

        if getSettingValue(sname) != None:
            return {'message': f'Setting {sname} already exists'}, 400

        mySettingsData.append({SettingName: sname, SettingValue: svalue})
        return {'message': 'Setting added successfully'}, 201

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5500, debug=False)

Running The Application

To run the application:

  • From the terminal execute the python file.
    python endPointGet.py

1

  • Open a web browser and navigate to http://127.0.0.1:5500, the page will show the swagger document for the Flask application.

  • Click default to expand the list of endpoints
    2

  • Click the POST button next to /Setting.
    3

  • Click the Try it out button.
    4

  • Enter a setting name, value, and click the Execute button.
    5

  • The response body should confirm the new setting has been added.
    6

  • Clicking the Execute button a second time will result in the error code 400, the setting already exists.
    7

Unit Testing The Application

The unit tests will check multiple scenarios when the setting name exists and when the setting name does not exist.

To unit test the flask application:

  • Create a text file called endPointPostTests.py and add the following code.
import unittest
import json
from endPointPost import *

class SystemSettingsTestCase(unittest.TestCase):
    def setUp(self):
        """Set up for unit tests, function is executed before tests begin"""

        self.client = app.test_client()
        app.config['TESTING'] = True

    def test_get_settings(self):
        """Test confirms that GET /Settings will return a list of all the system settings"""

        # Arrange 
        expected_result = [
            {'Name': 'Setting1', 'Value': 'Value1'},
            {'Name': 'Setting2', 'Value': 'Value2'}
        ]

        # Act
        response = self.client.get('/Settings') # Make a GET request to the /Settings endpoint

        # Assert
        self.assertEqual(response.status_code, 200) # Check that the response status code is 200 OK
        actual_result = json.loads(response.data) # Parse the JSON response        
        self.assertEqual(actual_result, expected_result) # Check that the response contains the expected data

    def test_post_setting(self):
        """Test confirms that POST /Setting will add a new setting"""

        # Arrange 
        expected_result = [
            {'Name': 'Setting1', 'Value': 'Value1'},
            {'Name': 'Setting2', 'Value': 'Value2'},
            {'Name': 'Setting3', 'Value': 'Value3'}
        ]

        # Act
        response = self.client.post('/Setting', json={"Name":"Setting3", "Value":"Value3"}) # Make a POST request to the /Setting endpoint

        # Assert
        print("post response", response)
        self.assertEqual(response.status_code, 201) # Check that the response status code is 201 CREATED
        response = self.client.get('/Settings') # Make a GET request to the /Settings endpoint to get all settings which should include the new setting
        actual_result = json.loads(response.data) # Parse the JSON response        
        self.assertEqual(actual_result, expected_result) # Check that the response contains the old settings and the new setting

    def test_post_setting_no_post_data(self):
        """Test confirms that POST /Setting will return 415 when post request does not contain the json data"""

        # Arrange 

        # Act
        response = self.client.post('/Setting') # Make a POST request to the /Setting endpoint without json data

        # Assert
        self.assertEqual(response.status_code, 400) # Check that the response status code is 415
        self.assertEqual(response.status, '400 BAD REQUEST') # Check the response status message   

    def test_post_setting_bad_post_data(self):
        """Test confirms that POST /Setting will return 415 when post request contain bad json data"""

        # Arrange 
        badJsonData = {'Bad':'bob1'} # Json data should include Name and Value
        expected_message = f'Input payload validation failed'

        # Act
        response = self.client.post('/Setting', json=badJsonData) # Make a POST request to the /Setting endpoint with bad json data

        # Assert
        self.assertEqual(response.status_code, 400) # Check that the response status code is 400
        self.assertEqual(response.status, '400 BAD REQUEST') # Check the response status message        
        response_json = response.get_json() # Extract the JSON data from the response    
        self.assertIn('message', response_json) # Ensure the 'message' key is in the response
        self.assertEqual(response_json['message'], expected_message) # Check that the response message contains the expected message

    def test_post_setting_exists_post_data(self):
        """Test confirms that POST /Setting will return 400 when setting already exists"""

        # Arrange 
        badJsonData = {"Name":"Setting1", "Value":"Value10"} # Json data contains an already exisiting setting name
        expected_message = f'Setting Setting1 already exists'

        # Act
        response = self.client.post('/Setting', json=badJsonData) # Make a POST request to the /Setting endpoint with json data

        # Assert
        self.assertEqual(response.status_code, 400) # Check that the response status code is 400
        self.assertEqual(response.status, '400 BAD REQUEST') # Check the response status message        
        response_json = response.get_json() # Extract the JSON data from the response    
        self.assertIn('message', response_json) # Ensure the 'message' key is in the response
        self.assertEqual(response_json['message'], expected_message) # Check that the response message contains the expected message


if __name__ == '__main__':
    unittest.main()
  • To run the test, execute the Python file endPointPostTests.py from the terminal.

python endPointPostTests.py

  • The log messages from a successful test will end with OK.
    8