From 45e1c5ff48393d20c4ed735699902388371cae68 Mon Sep 17 00:00:00 2001 From: kaizendae Date: Sun, 1 Dec 2024 13:00:32 +0100 Subject: [PATCH] feat: add article on ways to contribute to GeeksBlabla community --- .../10-ways-to-contribute-to-geeksblabla.md | 122 ++++++ articles/community-01.md | 363 ------------------ 2 files changed, 122 insertions(+), 363 deletions(-) create mode 100644 articles/10-ways-to-contribute-to-geeksblabla.md delete mode 100644 articles/community-01.md diff --git a/articles/10-ways-to-contribute-to-geeksblabla.md b/articles/10-ways-to-contribute-to-geeksblabla.md new file mode 100644 index 00000000..09e82bc1 --- /dev/null +++ b/articles/10-ways-to-contribute-to-geeksblabla.md @@ -0,0 +1,122 @@ +--- +title: "10 Ways to Contribute to GeeksBlabla and Make a Lasting Impact" +tags: ["community", "GeeksBlabla", "contributions", "tech"] +keywords: ["GeeksBlabla", "contribute", "Moroccan tech community"] +pubDatetime: 2024-12-01 +description: "Explore how you can contribute to GeeksBlabla, from sharing feedback to joining the core team, and make a meaningful impact in the Moroccan tech community." +--- + +**"How can I contribute to the GeeksBlabla community?"** This is one of the most common questions we receive, and this blog post is here to answer it. + +Contributing to GeeksBlabla goes beyond being a core member—it’s about making an impact in ways that resonate with you, whether it’s by sharing feedback, suggesting topics, or being a guest on an episode. Some contributions, like giving a talk at BlablaConf, create a ripple effect that continues to inspire others long after. + +Whether you're a student exploring the tech world or a seasoned professional looking to give back, this guide will show you how you can make a difference while growing alongside the community. Let’s get started! + +## 1. Provide Feedback + +- **Impact:** ⭐⭐⭐ +- **Difficulty:** ⭐ +- **Time Investment:** ⭐ + +Constructive feedback helps us improve. Whether it’s about our content, events, or initiatives, your input is invaluable in shaping the future of GeeksBlabla. + +Taking a few minutes to share your thoughts can lead to big improvements and better experiences for everyone involved. + +## 2. Spread the Word + +- **Impact:** ⭐⭐⭐⭐ +- **Difficulty:** ⭐ +- **Time Investment:** ⭐ + +Help us grow the community by sharing GeeksBlabla content and initiatives with your network. By sharing, you help us discover talented individuals who might become future podcast guests or BlablaConf speakers, enriching our community with valuable knowledge and diverse perspectives. The larger our reach, the more opportunities we create for knowledge sharing and the greater our collective impact on Morocco's tech ecosystem. + +Tag us when you share, and invite others to join the conversation! + +## 3. Participate in Online Discussions + +- **Impact:** ⭐⭐⭐ +- **Difficulty:** ⭐ +- **Time Investment:** ⭐ + +Whether it’s in the comments section of our live episodes or tweets by other community members on X or LinkedIn, sharing feedback on ideas and projects can create a ripple effect. Engaging in live episodes brings your perspective to light and enriches discussions. + +Similarly, your feedback amplifies the impact of others’ work. When you’re the one building, creating, or writing, the feedback you receive will push you forward. Cultivating the habit of providing and receiving feedback helps everyone grow together. + +## 4. Suggest Guests and Topics for Episodes + +- **Impact:** ⭐⭐⭐⭐ +- **Difficulty:** ⭐ +- **Time Investment:** ⭐ + +One of the best ways to contribute to the GeeksBlabla podcast is by suggesting topics and guests. This helps with one of our most challenging tasks, since we rely on a broad network of connections to find great guests. + +You might know ideal guests or niche topics that the core team hasn't discovered yet, helping us fill blind spots we wouldn't otherwise see. Plus, you'll benefit from other members' suggestions, discovering new topics and voices. + +Check our [planning here](#) to suggest an episode or a guest for an episode. + +## 5. Be a Guest on an Episode + +- **Impact:** ⭐⭐⭐⭐⭐ +- **Difficulty:** ⭐⭐⭐ +- **Time Investment:** ⭐⭐ + +If you have expertise in your field, consider being a guest on an episode. Share your knowledge, experiences, and insights to inspire and guide others in the community. + +Suggest a topic you’re passionate about and credible on—it’s an opportunity to showcase your expertise, mentor newcomers, and connect with an engaged audience. + +Check our [planning here](#) to suggest an episode or a guest for an episode. + +## 6. Create Valuable Content + +- **Impact:** ⭐⭐⭐⭐⭐ +- **Difficulty:** ⭐⭐⭐ +- **Time Investment:** ⭐⭐⭐ + +Creating content allows you to share what you’ve learned and help others in the process. Start small—write blog posts or social media tips about things you’ve discovered recently. + +Don’t worry about whether it’s “big” or “advanced”—many people can benefit from even the smallest insights. This also reinforces your own learning and contributes to the community’s knowledge base. + +## 7. Organize Meetups / Attend Meetups + +- **Impact:** ⭐⭐⭐⭐⭐ +- **Difficulty:** ⭐⭐⭐ +- **Time Investment:** ⭐⭐⭐ + +If you’re a student or part of a university/school, it’s the perfect time to organize or attend meetups. + +Hosting meetups brings like-minded people together, creates opportunities to network, and fosters collaboration. Invite speakers, host discussions, or simply connect with others to share knowledge and build a stronger local tech network. + +## 8. Contribute to Open Source + +- **Impact:** ⭐⭐⭐⭐⭐ +- **Difficulty:** ⭐⭐⭐⭐ +- **Time Investment:** ⭐⭐⭐ + +Open source contributions are a fantastic way to help the community while building your skills and portfolio. By contributing to GeeksBlabla’s website, projects listed on [Awesome Morocco](#), or even initiatives from other Moroccan tech enthusiasts, you gain real-world experience in coding, problem-solving, and collaborating with others. + +Beyond improving your technical skills, open source contributions expand your network, showcase your work to potential collaborators or employers, and give you the satisfaction of making a tangible impact on the community. + +## 9. Share Your Knowledge at BlablaConf + +- **Impact:** ⭐⭐⭐⭐⭐ +- **Difficulty:** ⭐⭐⭐⭐ +- **Time Investment:** ⭐⭐⭐⭐ + +BlablaConf, our annual conference, is the perfect platform to showcase your expertise and contribute to the Moroccan tech community. Whether you're an experienced professional or a rising talent, your talk can inspire, educate, and spark meaningful conversations. + +Submit your talk idea during our call for speakers, where you’ll have the chance to present your insights, share unique experiences, and leave a lasting impact on a diverse audience of tech enthusiasts. + +## 10. Join the Core Team + +- **Impact:** ⭐⭐⭐⭐⭐ +- **Difficulty:** ⭐⭐⭐⭐⭐ +- **Time Investment:** ⭐⭐⭐⭐⭐ +- **Commitment:** ⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐ + +The ultimate form of contribution is joining the core team. Core team members are dedicated volunteers who help drive GeeksBlabla's mission forward through consistent, long-term commitment. + +As a core team member, you'll help shape the community's direction, organize events, manage initiatives, and mentor others. This role requires significant dedication but offers unparalleled opportunities to develop leadership skills and make a lasting impact. + +Core team members typically start as active contributors who demonstrate exceptional commitment, initiative, and alignment with GeeksBlabla's values. + +> As Plato wisely noted, "The whole is greater than the sum of its parts." This ancient wisdom perfectly captures how individual contributions create ripple effects throughout our tech community. When members share knowledge, organize events, or participate in discussions, they're not just helping GeeksBlabla - they're strengthening the entire Moroccan tech ecosystem. Each action, no matter how small, creates waves of positive change that benefit everyone. Whether it's a student finding inspiration in a podcast episode, a professional expanding their network at BlablaConf, or a developer growing through open-source contributions, these individual efforts combine to foster an environment of collective growth, learning, and innovation. diff --git a/articles/community-01.md b/articles/community-01.md deleted file mode 100644 index 5ae40ba2..00000000 --- a/articles/community-01.md +++ /dev/null @@ -1,363 +0,0 @@ ---- -title: "React Query Auth Token Refresh" -tags: ["react", "react-query", "auth"] -keywords: ["react", "react-query", "auth"] -pubDatetime: 2023-11-28 -description: A complete guide on how to refresh auth token while using react-query ---- - -One common misunderstanding I've noticed is that people often view React Query primarily as a data-fetching library. I understand this perspective, as it is indeed the most common use case for the library. However, if you take a look at the [React Query home page](https://tanstack.com/query/v5/), you will notice that the main description for React Query from its maintainers is "Powerful asynchronous state management" and the library doesn't care about asynchronous implementation details as long as you return a promise. - -With that in mind, we can easily see that the title of this article is somehow incorrect. So instead of trying to find a way to refresh your auth token with React Query, you should focus on the data fetching implementation. In most cases, people tend to use `axios`, simple `fetch`, or any other library to fetch data from their API. So we should focus on how to refresh our auth token with those libraries instead of React Query. - -For this article, we will use `axios` as our data-fetching library with React Query. However, the same concept applies to any other library. Even though using `axios` is highly recommended as it has built-in support for interceptors, which we will use to refresh our authentication token. - -As part of this article, we will also cover how to write unit tests for our implementation. Make sure to read the whole article to get the most out of it. - -## Table of Content - -## Simple Project Setup - -To simplify and focus on the main topic, let's assume we have a basic app that retrieves user data from an API and displays it in the UI. - -```tsx title="src/client.ts" -import axios from "axios"; - -export const client = axios.create({ - baseURL: "https://test-api.example.com", -}); -``` - -```tsx title="src/use-me.tsx" -import type { AxiosError } from "axios"; -import { useQuery } from "@tanstack/react-query"; - -import { client } from "./client"; -import type { User } from "./types"; - -type Response = User; - -export const useMe = () => - useQuery({ - queryKey: ["me"], - queryFn: () => client.get(`/user/me`).then(res => res.data), - }); -``` - -```tsx title="src/app.tsx" -import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; -import * as React from "react"; -import {useMe} from "./use-me"; - -const queryClient = new QueryClient(); - -export const App = () => { - return ( - - - - ); -}; - -const UserView = () => { - const { data, isLoading, error } = useMe(); - - if(isLoading) return
Loading...
; - if(error) return
Something went wrong
; - - return
{data.name}
; -}; -``` - -As you can see, we have 3 files: - -- `client.tsx`: our `axios` instance that we will use to fetch data from the API. -- `use-me.tsx`: a custom `useMe` hook, which uses `useQuery` from React Query to fetch the user from the API. -- `app.tsx`: our main component that uses the `useMe` hook to display the user name through the `UserView` component. - -This setup is pretty simple, but to some extent, it's mainly how most production apps are structured. Now, let's see how we can implement the auth token refresh logic. - -## Refresh auth token implementation - -As you may gather from the introduction and to prove my point, we will not touch any React Query code here. Instead, all the work will be done in the client file `src/client.tsx`. - -The first step is to create two new functions to set the token header and remove it from our `axios` instance. - -```tsx title="src/client.ts" {6-8,10-13} -import axios from "axios"; -export const client = axios.create({ - baseURL: "https://test-api.example.com", -}); - -export const setHeaderToken = (token: string) => { - client.defaults.headers.common.Authorization = `Bearer ${token}`; -}; - -export const removeHeaderToken = () => { - //client.defaults.headers.common.Authorization = null; - delete client.defaults.headers.common.Authorization; -}; -``` - -Normally, those two functions should be called depending on the user's authentication state. On login, and as we have the token from the API, we need to set it in the headers of the `client` instance. On logout, we need to remove it. - -Now, we need to add a simple library named [`axios-auth-refresh`](https://github.com/Flyrell/axios-auth-refresh) to refresh our authentication token. This library is a basic `axios` interceptor that will be triggered whenever we receive a 401 error from the API. This library requires a refresh token function that we need to implement. - -```bash -pnpm add axios-auth-refresh -``` - -```tsx title="src/client.ts" {2,12-15} -import axios from "axios"; -import createAuthRefreshInterceptor from "axios-auth-refresh"; - -import { refreshAuth } from "./refresh-auth"; - -export const client = axios.create({ - baseURL: "https://test-api.example.com", -}); - -... - -createAuthRefreshInterceptor(client, refreshAuth, { - statusCodes: [401], // default: [ 401 ] - pauseInstanceWhileRefreshing: true, -}); -``` - -To implement the `refreshAuth` function, we need to create a new file called `refresh-auth.ts` when we mainly : - -1. Fetch the new token from the API. -2. On success, we set the new token in the failed request headers so that it can be retried. Additionally, we also need to set the new token in our Axios instance for future requests. -3. On failure, that means we can't get a new token from the API. We can return the error and probably redirect the user to the login page, for example, in such cases. - -```tsx title="src/refresh-auth.ts" -import { client, setHeaderToken } from "./client"; - -export const fetchNewToken = async () => { - try { - const token: string = await client - .get("https://test-api.example.com/refresh-token") - .then(res => res.data.token); - return token; - } catch (error) { - return null; - } -}; - -export const refreshAuth = async (failedRequest: any) => { - const newToken = await fetchNewToken(); - - if (newToken) { - failedRequest.response.config.headers.Authorization = "Bearer " + newToken; - setHeaderToken(newToken); - // you can set your token in storage too - // setToken({ token: newToken }); - return Promise.resolve(newToken); - } else { - // you can redirect to login page here - // router.push("/login"); - return Promise.reject(); - } -}; -``` - -That's it! Now, whenever we receive a 401 error from the API, the `refreshAuth` function will be triggered, and we will try to fetch a new token from the API. If we succeed, we will set the new token in the failed request headers and to our `axios` client instance and try again. If we are unable to obtain a new token, we return an error and redirect the user to the login page. - -We can end this article here, but let's be a good developer and try to add some unit tests to make sure that the code works as expected. - -## Write Unit tests - -Writing unit tests for a React application may require a whole article, but we will try to keep it simple here, and by the end, I will share you with a link to the complete code so you can explore it further. - -As a wise person once said, **"Don't test the implementation, test use cases."** So let's forget about how we implement this and figure out what are the use cases: - -- When everything is fine and we receive a 200 response from the API with user details, the `refreshAuth` function shouldn't be called (happy path). - -- When sending a request to the API without a header token, we should not call the `refreshAuth` function and should return an error. This assumes that the token header is required to obtain a new token from the API. - -- If the API returns an error other than 401, we should not call the `refreshAuth` function, and our hook will return the error.(e.g., 500 error) - -- When we receive a 401 error from the API, we should call the `refreshAuth` function, retry the request, and return the user details. - -- When we receive a 401 error from the API and are unable to obtain a new token, we should return an error. - -We can make sure all those use cases are working as expected by just testing the `useMe` hook as it's our main entry point to the API. So let's get started. - -To test the `useMe` hook, we are going to use [`react-testing-library`](https://testing-library.com/docs/react-testing-library/intro/) with `jest` to test the `useMe` hook and [`nock`](https://github.com/nock/nock) to mock the API. I assume that you are familiar with those libraries. If not, you can check the links above to learn more about them, or you can comment below if you think I should write more articles about testing. - -First, let's prepare a `react-query` wrapper for our hook. The only important part is that we need to disable retries for our tests. More details can be found [here](https://react-query.tanstack.com/guides/testing). - -```tsx title="src/test-utils.tsx" -import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; -import * as React from "react"; - -// its always good to create a separate instance of query client for tests while testing hooks -const createRQClient = () => { - return new QueryClient({ - defaultOptions: { - queries: { - // ✅ turns retries off - retry: false, - }, - }, - }); -}; - -export const createRQWrapper = () => { - const queryClientTest = createRQClient(); - return ({ children }: { children: React.ReactNode }) => ( - - {children} - - ); -}; -``` - -Next is to prepare our mock API. We will use `nock` to mock the API and return the expected response for each use case. - -As you may have noticed in the code below; we create multiple mocks for the same API to handle different scenarios. This allows us to easily test various situations without the need to repeatedly write the same code. For example, we can call the `mockUserAPI('success')` to mock a successful response from the API and `mockUserAPI('error')` to mock an error response. - -```tsx title="src/mock.ts" -import nock from "nock"; -import type { User } from "./types"; - -export const nockInstance = (options?: nock.Options) => - nock(`https://test-api.example.com`, options); - -const userMock: Partial = { - email: "test@email.com", - firstName: "test", - lastName: "user", -}; - -type StatusType = "success" | "error"; - -export const mockUserAPI = (status: StatusType) => { - if (status === "success") - return nockInstance() - .persist() // this is important as on refresh we are calling this api again - .get("/user/me") - .reply(function () { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - //@ts-ignore - if (this?.req?.headers.authorization === "Bearer valid-token") { - return [200, userMock]; - } else { - if (this?.req?.headers?.authorization?.length > 0) { - return [401, { message: "invalid-auth-header" }]; - } - return [403, { message: "no-auth-token" }]; - } - }); - else - return nockInstance() - .get("/user/me") - .reply(500, { message: "server-error" }); -}; - -export const mockRefreshTokenAPI = (status: StatusType) => { - if (status === "success") - return nockInstance() - .get("/refresh-token") - .reply(200, { token: "valid-token" }); - else - return nockInstance() - .get("/refresh-token") - .reply(403, { message: "token-refresh-error" }); -}; -``` - -Now, we can write our tests for our use cases - -```tsx title="src/refresh-auth.test.tsx" -import nock from "nock"; - -import { mockRefreshTokenAPI, mockUserAPI } from "./mocks"; -import { useMe } from "./use-me"; -import { removeHeaderToken, setHeaderToken } from "./client"; -import { renderHook, waitFor } from "@testing-library/react"; -import { createRQWrapper } from "./test-utils"; -import * as ApiUtils from "./refresh-auth"; - -const fetchNewTokenSpy = jest.spyOn(ApiUtils, "fetchNewToken"); - -afterEach(() => { - fetchNewTokenSpy.mockClear(); - removeHeaderToken(); - nock.cleanAll(); -}); - -describe("Refresh token Logic", () => { - it("Should return the correct user details if token is valid + no call to refresh token", async () => { - mockUserAPI("success"); - setHeaderToken("valid-token"); - const { result } = renderHook(() => useMe(), { - wrapper: createRQWrapper(), - }); - await waitFor(() => { - expect(result.current.isSuccess).toBe(true); - expect(result.current.data.email).toEqual("test@email.com"); - }); - - expect(fetchNewTokenSpy).toHaveBeenCalledTimes(0); - }); - - it("Should return error if token header do not exist + no call for refresh token api", async () => { - mockUserAPI("success"); - const { result } = renderHook(() => useMe(), { - wrapper: createRQWrapper(), - }); - await waitFor(() => { - expect(result.current.isError).toBe(true); - }); - - expect(fetchNewTokenSpy).toHaveBeenCalledTimes(0); - }); - - it("should return error if the the api return error different than 401 + no call for refresh token api", async () => { - mockUserAPI("error"); - const { result } = renderHook(() => useMe(), { - wrapper: createRQWrapper(), - }); - await waitFor(() => { - expect(result.current.isError).toBe(true); - }); - - expect(fetchNewTokenSpy).toHaveBeenCalledTimes(0); - }); - - it("should call refresh token on error 401 and return success after refreshing token successfully", async () => { - mockUserAPI("success"); - mockRefreshTokenAPI("success"); - setHeaderToken("invalid-valid-token"); - const { result } = renderHook(() => useMe(), { - wrapper: createRQWrapper(), - }); - await waitFor(() => { - expect(fetchNewTokenSpy).toHaveBeenCalledTimes(1); - expect(result.current.isSuccess).toBe(true); - expect(result.current.data.email).toEqual("test@email.com"); - }); - }); - - it("Should return error on refresh token call return error + refresh token should be called", async () => { - mockUserAPI("success"); - mockRefreshTokenAPI("error"); - setHeaderToken("invalid-valid-token"); - const { result } = renderHook(() => useMe(), { - wrapper: createRQWrapper(), - }); - await waitFor(() => { - expect(result.current.isError).toBe(true); - expect(fetchNewTokenSpy).toHaveBeenCalledTimes(1); - }); - }); -}); -``` - -As you can see, we wrote 5 tests to cover all the use cases. For each test, we mock the API response based on the use case and then we execute the `useMe` hook using `renderHook` from `react-testing-library`. Finally, we assert that the hook returns the expected result and that the `fetchNewToken` function is called when needed. - -👉 You can find the complete code on [GitHub](https://github.com/yjose/react-query-auth-token-refresh) - -That's it! I hope you enjoyed this article. Make sure to share it with your network so that other people can benefit from it as well. If you have any questions, feel free to comment below, and I will be happy to help.