GHosttp
Tags: gcp, development, serverless, express
Published: 2024-05-24 15:21:00

{% embed https://youtu.be/kinz7OA4zPQ %}

GHosttp is a tiny development server for developing GCP functions.
With GHosttp, you can choose the folder where your GCP functions reside, allowing you to start developing from there.
Your functions can be developed in Typescript and use the latest ESM features.

## Why?
The [functions framework](https://github.com/GoogleCloudPlatform/functions-framework-nodejs?tab=readme-ov-file) by Google does not offer a great developer experience when developing an entire backend using GCP.
Whenever you develop or test a new function, you must run the `npx @google-cloud/functions-framework --target=myFunction` command. Not only is that super long, but it also does not offer hot module reloading. 
What if the name changes?
You need to rerun the command. Do you really want to create a bash alias for this library?
Furthermore, their repository shows CommonJS syntax. What if you want to use ESM or Typescript? Since Gen2 is running on Node v20, you could and should leverage ESM. GHosttp is familiar with both ESM and Typescript.

### Cloud Run
The Gen2 GCP cloud functions use [ExpressJS](https://expressjs.com/) on top of Cloud Run. ExpressJS comes with a development server. So, why not directly develop it locally in Express and then put it in a Dockerfile? Well, that's a lot of work, and you must be familiar with Docker and Cloud Run. GCP Cloud Functions gen2 abstracts many things for you.

## Opinionated
This package is a bit opinionated about developing your GCP function, but I made it as close to the ExpressJS function as possible.
You must name your function `handler`, and it shall be exported. Below you find a Typescript version. 
```ts
import type { Request, Response } from '@google-cloud/functions-framework';

export const handler = async (req: Request, res: Response) => {
  res.set('Access-Control-Allow-Origin', '*');
  res.set('Access-Control-Allow-Methods', 'POST');

  // Because of CORS
  if (req.method === 'OPTIONS') {
    res.set('Access-Control-Allow-Methods', 'GET');
    res.set('Access-Control-Allow-Headers', 'Content-Type');
    res.set('Access-Control-Max-Age', '3600');
    res.status(204).send('');
    return;
  }

  // Destructering your request body
  const { message } = req.body;
  res.set('Content-Type', 'application/json');
  res.send(
    JSON.stringify({
      message,
    }),
  );
  return new Response(JSON.stringify(message));
};
```

## Requirements
- GCP account
- GCP functions gen2
- Node >= 20 (I haven't tried with 18, but it's a good point to upgrade)

## Getting Started
In your repository, just run `npx ghosttp --dir path/to/my/functions`. That's all. Per default, your backend will run on `http://localhost:3000`

```sh
  ➜ Local:    http://localhost:3000/
  ➜ Network:  use --host to expose

🚀 Loading server entry ./src/run-dev-server.ts GHosttp 12:21:58 AM
✅ Server initialized in 90ms                   GHosttp 12:21:58 AM
👀 Watching ./path/to/functions for changes     GHosttp 12:21:58 AM
ℹ Following endpoints are available             GHosttp 12:21:58 AM
ℹ /logger                                       GHosttp 12:21:58 AM
ℹ /run-dev-server                               GHosttp 12:21:58 AM
```

It watches whenever you make changes to your function or add a new function. This is great if you want to do Test-Driven-Development and get instant feedback.

Watch the video above to see it in action.
### Arguments
- `--dir` - Path to your functions (default `"."`)
- `--port` - Port where your localhost will run (default `3000`)

### PR and contributions are welcome
Find the repository here:
https://github.com/jolo-dev/ghosttp

## What's next?
I will add a development server for Flask and Go.
Furthermore, I want to add a new command, `npx ghost add-handler my-new-handler --typescript`, which scaffolds a new file.
I will also make a blog/video about deploying your ESM/Typescript GCP function ;)

Let me know what you think, and I will be happy to make any contribution 🤩