Skip to content

How to Contribute to Redwood.JS

This article was written over 18 months ago and may contain information that is out of date. Some content may be relevant but please refer to the relevant official documentation or available resources for the latest information.

This Dot Media is kicking off a brand new series of videos in 2022 to help developers learn how they can contribute code to some of the world’s most popular JavaScript frameworks and technologies. Subscribe to This Dot Media’s Youtube Channel.

In our first episode, I sat down with David Price one of the creators and maintainers of RedwoodJS! If you would like to watch this interview, you can find it here!

What is RedwoodJS?

RedwoodJS is an app framework built on React, Prisma, and GraphQL that is especially useful for startups interested in prototyping since it integrates everything someone would need to carry out fullstack development out of the box. These integrations include Storybook, Webpack/Babel, and more with zero configurations.

The goal of RedwoodJS is, in part, to allow developers to focus on what makes their applications unique and interesting by handling a lot of the configuration logic inherent to deploying fullstack applications.

David Price

David Price is the Founder of human(Ethos), a San Francisco-based consulting firm that helps product teams build cultures of performance, and currently works as a Developer in Residence at Preston-Warner Ventures. Beyond this, David is one of the Creators of Redwood.JS, and serves as a Maintainer.

Important Resources to Check Out Before Contributing

If you would like to learn more about the specific features offered by RedwoodJS, you can read up on the official documentation here.

You can check out the RedwoodJS repository here. David suggests that new users spend time in the packages folder since these are all the packages that will get published to npm.

And finally, if you would like to become engaged with other developers interested in RedwoodJS, you can join the official community server and/or the official discord server.

The Best Way to Get Started

Before contributing any code to RedwoodJS’ open-source repository, David Price suggests introducing yourself to and getting support from the larger RedwoodJS community using some of the resources above:

“The way to get started is to get the moral support in the community, and say you are interested in helping out! And they can direct you!”

Not only will that give new contributors an idea of what current projects are in the works and where they can help, but it can help them connect with other developers who will direct them to the most ability-appropriate tasks.

PRO-TIP from David: There is a file in the repo itself, called contributing.md, that shows you how to get started on actual contributions!

New contributors can also click on the “help wanted” tab in Github issues to see what work is needed, and available! There are also tags called “good first issue”, which are great entry points to the project. Another tag is “bug/confirmed”, which allows contributors to help with bugs!

Great at reviewing code? Code reviews support functional testing in the PRs! Inside of the CI checks, there is a link to Gitpod which will pull up a VScode virtual editor that creates a test project. You can use this to help run tests! Anyone can come into a PR, run a test, and go through the process to report back what worked and didn't work.

Attending RedwoodJS Contributor Meetups

Visit the community forums for RedwoodJS to learn more about how the community is structured. As mentioned above, there are a number of useful discussion boards where you can find information about announcements, docs, and opportunities to contribute. Once you are active on the forums, you will be eligible to receive invitations to private contributor meetups, but be sure to ask for one, since there may not be Core Contributors who are actively searching for folks to invite!

Contributor Success Stories

  • Peter Colapietro started opening and contributing to PRs for Hacktoberfest. Because of his consistent, high quality work since October, he now helps with the Storybook team, working on a really important PR!
  • There are collaborators working with diversity outreach for Redwood.JS, and they have been brainstorming a lot of ideas on how to get people involved! Multiple contributors have landed jobs after working with RedwoodJS. The community has been growing and evolving. Jobs are being created within the community.

This Dot is a consultancy dedicated to guiding companies through their modernization and digital transformation journeys. Specializing in replatforming, modernizing, and launching new initiatives, we stand out by taking true ownership of your engineering projects.

We love helping teams with projects that have missed their deadlines or helping keep your strategic digital initiatives on course. Check out our case studies and our clients that trust us with their engineering.

You might also like

Introduction to Zod for Data Validation cover image

Introduction to Zod for Data Validation

As web developers, we're often working with data from external sources like APIs we don't control or user inputs submitted to our backends. We can't always rely on this data to take the form we expect, and we can encounter unexpected errors when it deviates from expectations. But with the Zod library, we can define what our data ought to look like and parse the incoming data against those defined schemas. This lets us work with that data confidently, or to quickly throw an error when it isn't correct. Why use Zod? TypeScript is great for letting us define the shape of our data in our code. It helps us write more correct code the first time around by warning us if we are doing something we shouldn't. But TypeScript can't do everything for us. For example, we can define a variable as a string or a number, but we can't say "a string that starts with user_id_ and is 20 characters long" or "an integer between 1 and 5". There are limits to how much TypeScript can narrow down our data for us. Also, TypeScript is a tool for us developers. When we compile our code, our types are not available to the vanilla JavaScript. JavaScript can't validate that the data we actually use in our code matches what we thought we'd get when we wrote our TypeScript types unless you're willing to manually write code to perform those checks. This is where we can reach for a tool like Zod. With Zod, we can write data schemas. These schemas, in the simplest scenarios, look very much like TypeScript types. But we can do more with Zod than we can with TypeScript alone. Zod schemas let us create additional rules for data parsing and validation. A 20-character string that starts with user_id_? It's z.string().startsWith('user_id_').length(20). An integer between 1 and 5 inclusive? It's z.number().int().gte(1).lte(5). Zod's primitives give us many extra functions to be more specific about *exactly* what data we expect. Unlike TypeScript, Zod schemas aren't removed on compilation to JavaScript—we still have access to them! If our app receives some data, we can verify that it matches the expected shape by passing it to your Zod schema's parse function. You'll either get back your data in exactly the shape you defined in your schema, or Zod will give you an error to tell you what was wrong. Zod schemas aren't a replacement for TypeScript; rather, they are an excellent complement. Once we've defined our Zod schema, it's simple to derive a TypeScript type from it and to use that type as we normally would. But when we really need to be sure our data conforms to the schema, we can always parse the data with our schema for that extra confidence. Defining Data Schemas Zod schemas are the variables that define our expectations for the shape of our data, validate those expectations, and transform the data if necessary to match our desired shape. It's easy to start with simple schemas, and to add complexity as required. Zod provides different functions that represent data structures and related validation options, which can be combined to create larger schemas. In many cases, you'll probably be building a schema for a data object with properties of some primitive type. For example, here's a schema that would validate a JavaScript object representing an order for a pizza: ` Zod provides a number of primitives for defining schemas that line up with JavaScript primitives: string, number, bigint, boolean, date, symbol, undefined, and null. It also includes primitives void, any, unknown, and never for additional typing information. In addition to basic primitives, Zod can define object, array, and other native data structure schemas, as well as schemas for data structures not natively part of JavaScript like tuple and enum. The documentation contains considerable detail on the available data structures and how to use them. Parsing and Validating Data with Schemas With Zod schemas, you're not only telling your program what data should look like; you're also creating the tools to easily verify that the incoming data matches the schema definitions. This is where Zod really shines, as it greatly simplifies the process of validating data like user inputs or third party API responses. Let's say you're writing a website form to register new users. At a minimum, you'll need to make sure the new user's email address is a valid email address. For a password, we'll ask for something at least 8 characters long and including one letter, one number, and one special character. (Yes, this is not really the best way to write strong passwords; but for the sake of showing off how Zod works, we're going with it.) We'll also ask the user to confirm their password by typing it twice. First, let's create a Zod schema to model these inputs: ` So far, this schema is pretty basic. It's only making sure that whatever the user types as an email is an email, and it's checking that the password is at least 8 characters long. But it is *not* checking if password and confirmPassword match, nor checking for the complexity requirements. Let's enhance our schema to fix that! ` By adding refine with a custom validation function, we have been able to verify that the passwords match. If they don't, parsing will give us an error to let us know that the data was invalid. We can also chain refine functions to add checks for our password complexity rules: ` Here we've chained multiple refine functions. You could alternatively use superRefine, which gives you even more fine grained control. Now that we've built out our schema and added refinements for extra validation, we can parse some user inputs. Let's see two test cases: one that's bound to fail, and one that will succeed. ` There are two main ways we can use our schema to validate our data: parse and safeParse. The main difference is that parse will throw an error if validation fails, while safeParse will return an object with a success property of either true or false, and either a data property with your parsed data or an error property with the details of a ZodError explaining why the parsing failed. In the case of our example data, userInput2 will parse just fine and return the data for you to use. But userInput1 will create a ZodError listing all of the ways it has failed validation. ` ` We can use these error messages to communicate to the user how they need to fix their form inputs if validation fails. Each error in the list describes the validation failure and gives us a human readable message to go with it. You'll notice that the validation errors for checking for a valid email and for checking password length have a lot of details, but we've got three items at the end of the error list that don't really tell us anything useful: just a custom error of Invalid input. The first is from our refine checking if the passwords match, and the second two are from our refine functions checking for password complexity (numbers and special characters). Let's modify our refine functions so that these errors are useful! We'll add our own error parameters to customize the message we get back and the path to the data that failed validation. ` Now, our error messages from failures in refine are informative! You can figure out which form fields aren't validating from the path, and then display the messages next to form fields to let the user know how to remedy the error. ` By giving our refine checks a custom path and message, we can make better use of the returned errors. In this case, we can highlight specific problem form fields for the user and give them the message about what is wrong. Integrating with TypeScript Integrating Zod with TypeScript is very easy. Using z.infer<typeof YourSchema> will allow you to avoid writing extra TypeScript types that merely reflect the intent of your Zod schemas. You can create a type from any Zod schema like so: ` Using a TypeScript type derived from a Zod schema does *not* give you any extra level of data validation at the type level beyond what TypeScript is capable of. If you create a type from z.string.min(3).max(20), the TypeScript type will still just be string. And when compiled to JavaScript, even that will be gone! That's why you still need to use parse/safeParse on incoming data to validate it before proceeding as if it really does match your requirements. A common pattern with inferring types from Zod schemas is to use the same name for both. Because the schema is a variable, there's no name conflict if the type uses the same name. However, I find that this can lead to confusing situations when trying to import one or the other—my personal preference is to name the Zod schema with Schema at the end to make it clear which is which. Conclusion Zod is an excellent tool for easily and confidently asserting that the data you're working with is exactly the sort of data you were expecting. It gives us the ability to assert at runtime that we've got what we wanted, and allows us to then craft strategies to handle what happens if that data is wrong. Combined with the ability to infer TypeScript types from Zod schemas, it lets us write and run more reliable code with greater confidence....

D1 SQLite: Writing queries with the D1 Client API cover image

D1 SQLite: Writing queries with the D1 Client API

Writing queries with the D1 Client API In the previous post we defined our database schema, got up and running with migrations, and loaded some seed data into our database. In this post we will be working with our new database and seed data. If you want to participate, make sure to follow the steps in the first post. We’ve been taking a minimal approach so far by using only wrangler and sql scripts for our workflow. The D1 Client API has a small surface area. Thanks to the power of SQL, we will have everything we need to construct all types of queries. Before we start writing our queries, let's touch on some important concepts. Prepared statements and parameter binding This is the first section of the docs and it highlights two different ways to write our SQL statements using the client API: prepared and static statements. Best practice is to use prepared statements because they are more performant and prevent SQL injection attacks. So we will write our queries using prepared statements. We need to use parameter binding to build our queries with prepared statements. This is pretty straightforward and there are two variations. By default we add ? ’s to our statement to represent a value to be filled in. The bind method will bind the parameters to each question mark by their index. The first ? is tied to the first parameter in bind, 2nd, etc. I would stick with this most of the time to avoid any confusion. ` I like this second method less as it feels like something I can imagine messing up very innocently. You can add a number directly after a question mark to indicate which number parameter it should be bound to. In this exampl, we reverse the previous binding. ` Reusing prepared statements If we take the first example above and not bind any values we have a statement that can be reused: ` Querying For the purposes of this post we will just build example queries by writing them out directly in our Worker fetch handler. If you are building an app I would recommend building functions or some other abstraction around your queries. select queries Let's write our first query against our data set to get our feet wet. Here’s the initial worker code and a query for all authors: ` We pass our SQL statement into prepare and use the all method to get all the rows. Notice that we are able to pass our types to a generic parameter in all. This allows us to get a fully typed response from our query. We can run our worker with npm run dev and access it at http://localhost:8787 by default. We’ll keep this simple workflow of writing queries and passing them as a json response for inspection in the browser. Opening the page we get our author results. joins Not using an ORM means we have full control over our own destiny. Like anything else though, this has tradeoffs. Let’s look at a query to fetch the list of posts that includes author and tags information. ` Let’s walk through each part of the query and highlight some pros and cons. ` * The query selects all columns from the posts table. * It also selects the name column from the authors table and renames it to author_name. * It aggregates the name column from the tags table into a JSON array. If there are no tags, it returns an empty JSON array. This aggregated result is renamed to tags. ` * The query starts by selecting data from the posts table. * It then joins the authors table to include author information for each post, matching posts to authors using the author_id column in posts and the id column in authors. * Next, it left joins the posts_tags table to include tag associations for each post, ensuring that all posts are included even if they have no tags. * Next, it left joins the tags table to include tag names, matching tags to posts using the tag_id column in posts_tags and the id column in tags. * Finally, group the results by the post id so that all rows with the same post id are combined in a single row SQL provides a lot of power to query our data in interesting ways. JOIN ’s will typically be more performant than performing additional queries.You could just as easily write a simpler version of this query that uses subqueries to fetch post tags and join all the data by hand with JavaScript. This is the nice thing about writing SQL, you’re free to fetch and handle your data how you please. Our results should look similar to this: ` This brings us to our next topic. Marshaling / coercing result data A couple of things we notice about the format of the result data our query provides: Rows are flat. We join the author directly onto the post and prefix its column names with author. ` Using an ORM we might get the data back as a child object: ` Another thing is that our tags data is a JSON string and not a JavaScript array. This means that we will need to parse it ourselves. ` This isn’t the end of the world but it is some more work on our end to coerce the result data into the format that we actually want. This problem is handled in most ORM’s and is their main selling point in my opinion. insert / update / delete Next, let’s write a function that will add a new post to our database. ` There’s a few queries involved in our create post function: * first we create the new post * next we run through the tags and either create or return an existing tag * finally, we add entries to our post_tags join table to associate our new post with the tags assigned We can test our new function by providing post content in query params on our index page and formatting them for our function. ` I gave it a run like this: http://localhost:8787authorId=1&tags=Food%2CReview&title=A+review+of+my+favorite+Italian+restaurant&content=I+got+the+sausage+orchette+and+it+was+amazing.+I+wish+that+instead+of+baby+broccoli+they+used+rapini.+Otherwise+it+was+a+perfect+dish+and+the+vibes+were+great And got a new post with the id 11. UPDATE and DELETE operations are pretty similar to what we’ve seen so far. Most complexity in your queries will be similar to what we’ve seen in the posts query where we want to JOIN or GROUP BY data in various ways. To update the post we can write a query that looks like this: ` COALESCE acts similarly to if we had written a ?? b in JavaScript. If the binded value that we provide is null it will fall back to the default. We can delete our new post with a simple DELETE query: ` Transactions / Batching One thing to note with D1 is that I don’t think the traditional style of SQLite transactions are supported. You can use the db.batch API to achieve similar functionality though. According to the docs: Batched statements are SQL transactions ↗. If a statement in the sequence fails, then an error is returned for that specific statement, and it aborts or rolls back the entire sequence. ` Summary In this post, we've taken a hands-on approach to exploring the D1 Client API, starting with defining our database schema and loading seed data. We then dove into writing queries, covering the basics of prepared statements and parameter binding, before moving on to more complex topics like joins and transactions. We saw how to construct and execute queries to fetch data from our database, including how to handle relationships between tables and marshal result data into a usable format. We also touched on inserting, updating, and deleting data, and how to use transactions to ensure data consistency. By working through these examples, we've gained a solid understanding of how to use the D1 Client API to interact with our database and build robust, data-driven applications....

This is the Worst Thing a DevRel Team Could Do cover image

This is the Worst Thing a DevRel Team Could Do

At its core, a successful Developer Relations (DevRel) program focuses on forming strong connections within its target audience to ensure that developers can easily connect with the company or organization behind the product they’re using. Great teams in DevRel build genuine relationships, earn trust, and actively engage with developers. DevRel takes a different approach from traditional marketing strategies. Instead of just chasing numbers and leads for sales, it puts emphasis on making developers happy and retention. This creates a cycle of feedback between users and a company, helping to better understand their needs and fostering a sense of community among users. There have been organizations that consider DevRel a revenue-driving function, but this is where challenges arise, since DevRel teams cannot consistently show value in this area and meet the expectations of stakeholders expecting this based on where their focus and the profession lies. When DevRel professionals are forced to be revenue drivers, the breakdown between developers and that company is inevitable. Developers can smell hidden agendas from a mile away. They realize when their needs are being overlooked in favor of sales initiatives. *“Companies fail at DevRel when they try to turn them into sales teams. This hurts customer trust.” - Michael Liendo, Senior Developer Advocate at AWS. * Developers care about education, resources, opportunities, and the overall experience of a product or platform. When they are a target for sales pitches, they leave or shut down. Their focus is getting the job done, and they talk to companies to solve problems in their development lifecycle, not to be on the receiving end of a pitch. Furthermore, since all successful DevRel teams are focused on building authentic relationships with developers, strategic partnerships with other organizations will be hard to come by or have dismal retention numbers since no one wants to be associated with trying to sell to developers. Good DevRel teams are focused on helping developers and understand that being helpful goes a long way to help the sales cycle. Many DevRel efforts also focus on providing value to a community before asking for something in return. The trust and authenticity of brand building in this area and those initiatives - the loyalty a DevRel team is trying to generate - many times does not make direct corollary sense from a monetary perspective, and sales organizations often overlook the non-monetary value and impact these efforts generate and how they correlate to long-term growth for an organization. In DevRel, genuine connections matter more than immediate revenue. When teams prioritize developers' needs over sales, trust grows, fostering lasting relationships that drive long-term success. Treating DevRel as solely a sales function erodes trust and misses the true value of building authentic partnerships and community loyalty. Interested in learning more about launching your own DevRel program, feel free to reach out!...

“Recognize leadership behavior early. Sometimes people don’t even realize it in themselves…” Kelly Vaughn on Product Leadership, Creating Pathways for Women in Tech, & Conferences cover image

“Recognize leadership behavior early. Sometimes people don’t even realize it in themselves…” Kelly Vaughn on Product Leadership, Creating Pathways for Women in Tech, & Conferences

Some leaders build products. Some lead engineering teams. Kelly Vaughn is doing both. As Director of Engineering at Spot AI—a company building video intelligence software—Kelly recently expanded her role to oversee both Product and Engineering for their VMS offering. That shift means juggling strategy, execution, and team development, all while helping others step confidently into leadership themselves. And yes, she still finds time to speak at conferences and answer DMs from people navigating the same transitions she once did. We spoke with Kelly about spotting leadership potential early, why ambiguity doesn’t have to feel chaotic, and the lesson she learned the hard way about managing up. Stepping into Product Leadership Kelly’s new title might look like a promotion on paper, but the shift is more philosophical than anything. > “Engineering leadership is about execution,” she says. “Product leadership is about defining why we’re building something in the first place.” Now leading Product and Engineering for Spot AI’s VMS product, she’s talking to customers, researching market trends, and making smart bets on where to invest next. It’s a role she’s clearly energized by. > “I’m really looking forward to dedicating time to shaping our product’s future.” Thriving in Ambiguity Some people panic when problems are fuzzy or undefined. Others use it as fuel. > “There are two key traits I see in people who handle ambiguity well,” Kelly says. “They stay calm under stress, and they know how to form a hypothesis from a vague problem statement.” That means asking the right questions, taking action quickly, and being totally okay with pivoting when something doesn’t pan out. It’s no surprise that these same traits overlap with great product thinking—a mindset she’s now leaning into more than ever. > “I do some of my best work when navigating uncertainty,” she adds. Read Kelly’s blog on embracing ambiguity in Product! Creating Leadership Pathways for Women in Tech When asked how leaders can create more leadership pathways for women in software engineering, Kelly stressed that it is not a passive process. > “Senior leaders need to be proactive,” Kelly says. “That starts with identifying and addressing bias across hiring, promotions, and day-to-day interactions.” She emphasizes psychological safety—so women feel confident advocating for themselves and others. But she also knows not everyone feels ready to raise their hand. > “Don’t wait for someone to ask for a title change or a growth opportunity. Recognize leadership behavior early. Sometimes people don’t even realize it in themselves yet.” On Stage, In Real Life Kelly’s no stranger to the tech conference circuit—often giving talks on engineering leadership and team growth. Her biggest source of inspiration? Conversations with people trying to make the leap into leadership. > “I might use the same slide deck at three conferences,” she says, “but the talk itself will be different every time.” Rather than sticking to a script, she likes to share recent examples from her own work, tailoring the delivery to the audience in front of her. It keeps things relevant, grounded, and never too polished. Between setting product strategy, mentoring the next generation of leaders, and hopping from one tech conference to the next, Kelly Vaughn is showing what it means to lead with clarity—even when things are unclear. She’s not here to tell you it’s easy. But she will tell you it’s worth it. Connect with Kelly Vaughn on Bluesky. Sign up for Kelly Vaughn’s Newsletter! Sticker Illustration by Jacob Ashley....

Let's innovate together!

We're ready to be your trusted technical partners in your digital innovation journey.

Whether it's modernization or custom software solutions, our team of experts can guide you through best practices and how to build scalable, performant software that lasts.

Prefer email? hi@thisdot.co