Awesome
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
- AWS deployment with Architect
- Hyper Cloud integration via
hyper-connect
- Zero-setup ⚡️ local development using
hyper nano
- Hyper Vision support, to peer into your hyper services
- GitPod integration for developing in ephermeral cloud environments
- GitHub Actions for deploy on merge to production and staging environments
- Email/Password Authentication with cookie-based sessions
- Styling with Tailwind
- End-to-end testing with Cypress
- Local third party request mocking with MSW
- Unit testing with Vitest and Testing Library
- Code formatting with Prettier
- Linting with ESLint
- Static Types with TypeScript
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
- The application should at least have a hyper data service
- 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.
- creating users, finding a user, and deleting a user and their data ./app/services/user.server.ts
- creating, and deleting notes ./app/services/note.server.ts
- user sessions, and verifying them ./app/session.server.ts
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
forstaging
andproduction
via your GitHub repo's secrets as part of CI. See thedeploy
step in yourdeploy
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
andAWS_SECRET_ACCESS_KEY
andAWS_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 anARC_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
andSESSION_SECRET
via your GitHub repo's secrets as part of CI. See thedeploy
step in yourdeploy
workflowIf 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!