Skip to content

hyper63/alternative-stack

Repository files navigation

Alternative Stack

The Alternative Stack

The Alternative Stack is a Remix Stacks using Hyper as a services tier

Learn more about Hyper

Learn more about Remix Stacks.

Blog Post

Check out our blog post on the Alternative Stack

npx create-remix --template hyper63/alternative-stack

What's in the stack

Not a fan of bits of the stack? Fork it, change it, and use npx create-remix --template your/repo! Make it your own.

Prerequisites

This Remix stack sets up a local development stack using hyper nano ⚡️ an in-memory instance of hyper. hyper nano works great for local development or short-lived, ephemeral environments like GitHub Workspaces or GitPod

At hyper, we exclusively develop using short-lived ephemeral environments

If you choose not to use hyper nano, you will need to create a hyper cloud application on hyper cloud:

  • Create a free starter account on hyper cloud
  • Create a free hyper cloud application. Learn more here
  • Take your application's connection string and use it to set your HYPER environment variable in your .env, during setup. Learn more here

Development

  • Start dev server:

    npm run dev

This starts your app in development mode, rebuilding assets on file changes. All data will be persisted to your hyper application based on your HYPER connection string environment variable.

Relevant code:

This is a pretty simple note-taking app, but it's a good example of how you can build a full stack app with Hyper and Remix, and deploy it using Architect. The main functionality is creating users, logging in and out, and creating and deleting notes.

Gitpod Integration

The Alternative Stack comes with support for cloud based development, using GitPod. Just initialize the Alternative Stack using the Remix CLI, push to Github, and then open in Gitpod by visiting:

https://gitpod.io/#your-repo-url

You can also use Gitpod's browser extension

This will build a containerized cloud environment, install dependencies, and start up your services for you, a complete sandboxed environment. We use a GitPod .gitpod.yml to set up our Cloud environment and expose our services.

Botch a feature and need to wipe the data? Just open a new Gitpod!

Need to fix a bug? Create an issue and then open the issue in Gitpod!

With Gitpod, you no longer have to maintain a local environment. Just spin up a new one every time!

Hyper Sevice Vision 🕶

If you'd like to see what is being stored in your hyper services, you can use Hyper Vision to peer into your services.

Hyper Vision is a hosted, read-only, hyper service explorer that you can use to view your services. Just provide Hyper Vision with your application's hyper connection string (process.env.HYPER) and it will introspect your services!

If you're running hyper nano locally, you will need to use a proxy to make it accessible on the internet. ngrok is a great tool for this (though if you develop in ephemeral cloud environments like Gitpod, you get this out of the box 😎).

Clean Architecture With Hyper

Hyper embraces the Clean Architecture approach to building software. This means separating side effects from business logic and striving to keep business logic separated from other details of the application.

This has lots of benefits:

  • Business logic is framework agnostic
  • Business logic is infrastructure agnostic
  • Easier to test business logic (unit tests and TypeScript cover most of it!)
  • Separation of concerns

All of the business logic for this application can be found in ./app/services. Each service receives its side effects via dependency injection which are then easy to stub during unit testing.

Our business models are simple schemas built on zod used to validate the correctness of data flowing in and out of our business logic layer.

You could use anything to validate the contracts with your business logic, I chose zod because it's what we use at hyper. Joi, Yup, there are tons of options out there.

Because all side effects are injected via dependency injection, the business logic is incredibly easy to test. The business logic is practically fully tested using just unit tests. (run npm run test --coverage to see for yourself)

You can also see dependency injection in ./server.ts, which uses Remix's getLoadContext to inject our business services and session handling into our loaders and actions, via context.

Learn more from our blog post

Deployment

This Remix Stack comes with two GitHub Actions that handle automatically deploying your app to production and staging environments. By default, Arc will deploy to the us-west-2 region, if you wish to deploy to a different region, you'll need to change your app.arc

Alternatively, you can set AWS_REGION in your GitHub repo's secrets

Hyper Setup

Each environment will need a hyper cloud application backing it. Since this stack has a staging and production environment, you will need a hyper cloud application for staging and then another for production (you get 3 hyper cloud applications for free, and you can always upgrade them later). Learn more here.

Once the hyper applications have been created, set the HYPER environment variable in production and staging environments using Arc:

npx arc env --add --env staging HYPER cloud://....
npx arc env --add --env production HYPER cloud://....

Alternatively, you can set HYPER for staging and production via your GitHub repo's secrets as part of CI. See the deploy step in your deploy workflow

Architect Setup

Prior to your first deployment, you'll need to do a few things:

  • Sign up and login to your AWS account

  • Add AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY and AWS_REGION to your GitHub repo's secrets. Go to your AWS security credentials and click on the "Access keys" tab, and then click "Create New Access Key", then you can copy those and add them to your repo's secrets.

  • Along with your AWS credentials, you'll also need to give your CloudFormation a SESSION_SECRET variable of its own for both staging and production environments, as well as an ARC_APP_SECRET for Arc itself.

    npx arc env --add --env staging ARC_APP_SECRET $(openssl rand -hex 32)
    npx arc env --add --env staging SESSION_SECRET $(openssl rand -hex 32)
    npx arc env --add --env production ARC_APP_SECRET $(openssl rand -hex 32)
    npx arc env --add --env production SESSION_SECRET $(openssl rand -hex 32)

    Alternatively, you can generate and set ARC_APP_SECRET and SESSION_SECRET via your GitHub repo's secrets as part of CI. See the deploy step in your deploy workflow

    If you don't have openssl installed, you can also use 1password to generate a random secret, just replace $(openssl rand -hex 32) with the generated secret.

Where do I find my CloudFormation?

You can find the CloudFormation template that Architect generated for you in the sam.yaml file.

To find it on AWS, you can search for CloudFormation (make sure you're looking at the correct region!) and find the name of your stack (the name is a PascalCased version of what you have in app.arc, so by default it's AlternativeStackStaging and AlternativeStackProduction) that matches what's in app.arc, you can find all of your app's resources under the "Resources" tab.

GitHub Actions

We use GitHub Actions for continuous integration and deployment. Anything that gets into the main branch will be deployed to production after running tests/build/etc. Anything in the dev branch will be deployed to staging.

Testing

Cypress

We use Cypress for our End-to-End tests in this project. You'll find those in the cypress directory. As you make changes, add to an existing file or create a new file in the cypress/e2e directory to test your changes.

We use @testing-library/cypress for selecting elements on the page semantically.

To run these tests in development, run npm run test:e2e:dev which will start the dev server for the app as well as the Cypress client. Make sure the database is running in docker as described above.

We have a utility for testing authenticated features without having to go through the login flow:

cy.login();
// you are now logged in as a new user

We also have a utility to auto-delete the user at the end of your test. Just make sure to add this in each test file:

afterEach(() => {
  cy.cleanupUser();
});

That way, we can keep your local db clean and keep your tests isolated from one another.

Vitest

For lower level tests of utilities and individual components, we use vitest. We have DOM-specific assertion helpers via @testing-library/jest-dom.

Type Checking

This project uses TypeScript. It's recommended to get TypeScript set up for your editor to get a really great in-editor experience with type checking and auto-complete. To run type checking across the whole project, run npm run typecheck.

Linting

This project uses ESLint for linting. That is configured in .eslintrc.js.

Formatting

We use Prettier for auto-formatting in this project. It's recommended to install an editor plugin (like the VSCode Prettier plugin) to get auto-formatting on save. There's also a npm run format script you can run to format all files in the project.

Thank You

We at hyper are very excited about Remix and Remix stacks. A huge shout out to the Remix team and to Kent C. Dodds for showing us this cool new feature.

Also thank you to the all the maintainers of open source projects that we use. In particular:

and many many more!